`
阅读更多

2.2 COM 模型的原始实现

描述:COM是一个结构模型,它用来实现面向对象的、与语言无关的、且位置透明的组件或软件模块,Microsoft对COM的实现是一套短小的Win32API函数和大量的COM接口声明.
    
2.2.1 客户程序和服务器程序

服务器 -- 为其他软件实体提供一些功能的COM组件,或实现某一接口的软件模块.
客户机 -- 使用组件功能的软件实体,或使用接口的软件模块

在多数情况下,一个组件既可以是一定数量的COM接口的实现者,也可以是一个使用者


2.2.2 OLE 和 ActiveX

简单地说,COM是一个系统级别的标准,它提供了基本的对象模型服务,是一个软件交互操作标准,OLE 和 ActiveX是高级别服务的范例,是建立在软件交互操作标准之上的高级别服务.


2.2.3 Microsoft Transaction Server(MTS)

Microsoft 事务处理服务器(MTS) -- 提供一个针对中间组件的执行环境
COM+ -- COM 和 MTS编程模型的融合

 

2.3 COM 的精髓: 接口

描述: COM 的实现包括了一些Win32 API,其目的是为了引入COM环境.COM的主要工作是处理接口的实现和使用上的问题.一个COM接口同C++类的公共接口非常相似,COM采用一种与语言无关的,位置透明的方式描述方法和属性,以及把内部实现的细节完全封装起来.

2.3.1 C++ 的虚函数表

COM接口是通过 C++的 Vtable(虚函数表) 建立起来的.虚函数表提供了类实例功能的滞后绑定.

2.3.1.1 纯虚函数和抽象类

1)一个抽象类(Abstract Class)为所有派生类提供了一个模型或摸板.如:

class Fruit
{
public:
 void put_Color(string str)
 {
  m_strColor = str;
 }
 
 string get_Color()
 {
  return m_strColor;
 }
 
 virtual void Draw() {};

private:
 string m_strColor;
};

水果本身就是一个抽象的东西,我们无法列举出一个通用的水果对象.

2) 开发者使用抽象类来把具有相似特性的东西进行分类和归类
3) 抽象类提供的属性和行为可以被所有的派生类所分享.
4) 通过声明一个纯虚函数使类变得抽象,这意味着类不能直接被实现,但你还是可以使用指向该类的指针


2.3.1.2 Vtable

后绑定 -- 在运行时绑定函数的地址.
前绑定 -- 在编译时绑定函数的地址.

1) 当你把一个函数声明为虚函数时,编译器将在你的类结构里加上一个指针,即虚指针vptr.vptr指向一个Vtable结构,
   该结构包含了类中所有虚函数的地址,也包含了基类.
2) java 和 Smalltalk,都对所有函数进行了滞后绑定,C++ 则是让程序员决定对哪个函数进行滞后绑定,这主要出于性能考虑,比如:vptr的初始化,查找等..


2.3.1.3 COM 如何使用Vtable

1) C++ 使用 Vtable 来实现多态功能,COM 则使用C++ 的 Vtable 结构来建立COM接口.
2) COM 接口实际就是一个指向Vtable结构的指针,因此它也只接纳方法.一个COM接口的用户只能访问组件的公共方法.
3) 示例:把 C++ 的Math类转化为一个COM组件,并通过组件的公共方法把一个基本接口向外部公开出来.

class Math
{
public:
 long Add(long ,long);
 long Subtract(long,long);
 long Multiply(long,long);
 long Divide(long,long);
private:
 //implementation here...
};


2.3.2 COM 的接口

1) 建立一个Vtable公开组件的公共接口

class IMath
{
public:
 virtual long Add(long,long) = 0;
 virtual long Subtract(long,long) = 0;
 virtual long Multiply(long,long) = 0;
 virtual long Divide(long,long) = 0;
};

2) 派生出子类并提供各自的实现

class Math:public IMath
{
public:
 long Add(long Op1,long Op2);
 long Subtract(long Op1,long Op2);
 long Multiply(long Op1,long Op2);
 long Divide(long Op1,long Op2);
};

3)基类里虚函数的使用是COM设计的核心,抽象类定义提供了一个仅仅包含类的公共方法(接口)的Vtable.它唯一的目的是强制派生类以虚函数的方式实现组件的接口方法.

4)COM 只能通过一个Vtable指针类提供对它组件的访问,所以对组件实现的访问是无法完成的.


2.4 对 COM 接口的访问

描述:COM API 中最重要的一个函数是CoCreateInstance,客户应用程序在创建组件实例时使用.

// Create an instance and return the IMath interface
IMath *pMath;

HRESULT hr = CoCreateInstance(CLSID_Math, -- 类标识符
   NULL,
   CLSCTX_INPROC,  -- 类上下文
   IID_IMath,  -- 接口标识符
   (void **) &pMath ); -- 返回接口
// Use IMath
long lResult = pMath->Multiply(44,33);

访问接口的另一方法

IMath *pMath;
hr = pUnk->QueryInterface(IID_IMath,(void **)&pMath);
pUnk->Release();

if (FAILED(hr))
{
 cout << "QueryInterface() for IMath failed" << endl;
 CoUninitialize();
 return -1;
}


2.5 组件的多接口特性

描述:COM 最重要的特性之一是赋予某一组件实现多个接口的能力,每一个接口都把小的,准确定义的功能集展示出来.然后,使用组件的客户可以直接与所需的功能模块打交道.
    
2.5.1 标准 COM 接口

1) 所有的COM组件都需要实现一个被称为IUnknown的标准COM接口(WINDOWS.H中引入)
2) IUnknown有两项功能:
   QueryInterface -- 用户可以在指定的组件里要求使用一个特定的接口
   AddRef,Release -- 帮助组件在生存期内进行管理
3) IUnknown 的定义:
class IUnknown
{
 virtual HRESULT QueryInterface(REFID riid,void ** ppv) = 0;
 virtual ULONG AddRef() = 0;
 virtual ULONG Release() = 0;
};

参数说明:
REFID -- 特定接口ID的引用,它是我们查询某一接口时所使用的特有标识符
ppv   -- 接口指针返回位置
HRESULT -- 指向COM所定义的错误结构,它包含了所有错误信息


2.5.2 HRESULT

1) HRESULT 被定义为DWORD类型(32位双字类型),高位表示函数调用是否成功,接着15位用来表示设备,并提供了一种对相关的返回代码进行分组的方式,这种分组方式是根据Windows错误生成子系统的实现.
2) COM中判断对方法调用是否成功的宏,SUCCEEDED 与 FAILED.补充一点,这些宏并非COM和ActiveX所特有,而是Win32环境里普遍使用的.在WINERROR.H文件中定义,调用成功,Win32里的返回值将具有前缀S_,否则E_.


2.5.3 IUnknown 的实现

1) COM 需要IUnknown接口在任何COM对象中出现,并且所有的COM接口都包含IUnknown接口(这样可以获得任意指定接口的指针)

// public interface definition
// An abstract class that derives from IUnknown
class IMath : public IUnknown
{
public:
 virtual long Add(long Op1,long Op2) = 0;
 virtual long Subtract(long Op1,long Op2) = 0;
 virtual long Multiply(long Op1,long Op2) = 0;
 virtual long Divide(long Op1,long Op2) = 0;
};

class IAdvancedMath : public IUnknown
{
public:
 virtual long Factorial(short sOp) = 0;
 virtual long Fibonacci(short sOp) = 0;
};

class Math: public IMath,public IAdvancedMath {};

// 实现 QueryInterface 方法
HRESULT Math::QueryInterface(REFID riid,void **ppv)
{
 *ppv = 0;
 if (riid == IID_IUnknown)
  *ppv = (IMath*)this;
 else if (riid == IID_IMATH)
  *ppv = (IMath *)this;
 else if (riid == IID_IAdvancedMath)
  *ppv = (IAdvancedMath*)this;
 
 if (*ppv)
 {
  (IUnknown*)(*ppv)->AddRef();
  return (S_OK);
 }
 return (E_NOINTERFACE);
}

2) 客户通过COM进行实例化并访问组件功能
// Create an instance an retrieve its IUnknown interface
IUnknown* pUnk;

HRESULT hr = CoCreateInstance(CLSID_Math,
   NULL,
   CLSCTX_INPROC,
   IID_IUnknown,
   (void **)&pUnk);
// Query for IMath
IMath* pMath;
pUnk->QueryInterface(IID_IMath,(void **)&pMath);

// Use IMath
long lResult = pMath->Multiply(44,33);


2.5.4 组件的生存期

1) 对一个组件接口的访问通过 IUnknown::QueryInterface完成的
2) 组件的生成管理由 IUnknown::AddRef,Release 所提供的方法加以管理
3) COM组件的一个实例可以有多个接口连接到多个客户上,所以需要使用一种引用计数的功能.

Math::Math()
{
 // Initialize our reference counter
 m_lRef = 0;
}

ULONG Math::AddRef()
{
 return InterlockedIncrement(&m_lRef);
}

ULONG Math::Release()
{
 // 计数值为0时删除自身
 if (InterlockedDecrement(&m_lRef) == 0)
 {
  delete this;
  return 0;
 }
 
 return m_lRef;
}
4)组件的用户是否能够在合适的时候,并且正确地调用AddRef 和 Release 来增大和减小组件的引用记数器非常重要.它类似于new/delete
5)QueryInterface总是返回一个接口指针之前调用AddRef,所以使用它的时候无须再调用AddRef.


2.5.5 全局特有标识符

1)在分布式对象和基于组件的环境里,组件和它们的接口的独有标识符是最为重要的.
2)通用独有标识符(UUID)是一个独有的、128位的、并且具有非常高的可靠率的数值.它把一个独有的网络地址(48)位和一个非常精细的时间戳(100纳秒)结合在一起
3)COM对UUID的实现称为全局特有标识符(GUID).COM使用GUID来标识组件的类(CLSID),接口(IID),类型库和组件类属(CATID)以及其他的一些东西.
4)DEFINE_GUID宏创建一个全局常量,你可以在程序里到处使用该常量,或客户端或服务器端,然而你只能定义一次(INITGUID.H中)
//{A888F560-58E4-11d0-A68A-0000837E3100}
DEFINE_GUID(CLSID_Math,0xA888F560,0x58E4,0x11d0,0xa6,0x8a,0x0,
  0x0,0x83,0x7e,0x31,0x0);
DEFINE_GUID(IID_Math,....)
DEFINE_GUID(IID_...)

5)也可以使用CoCreateGuid函数生成GUID,或 Visual C++ 中UUIDGEN工具
6) 常用GUID辅助函数
函数名称     功能
CoCreateGuid(GUID*pGuid)  采用鞭策的方法生成一个独有的GUID
isEqualGUID(REFGUID,REFGUID)  比较两个GUID
isEqualIID(REFIID,REFIID)  比较两个IID
isEqualCLSID(REFCLSID,REFCLSID)  比较两个CLSID
CLSIDFromProgID(LPCOLESTR,LPCLSID) 为给定的ProgID返回CLSID
ProgIDFromCLSID(REFCLSID,LPOLESTR *) 从CLSID里返回ProgID


2.5.6 标题标识符

组件由它的CLSID来唯一标识的,但是要记住组件的CLSID并不容易.COM用标题标识符或ProgID类对组件进行命名.一个简单的ProgID是一个简单的字符串,它通过注册表和组件的CLSID相关联.


2.5.7 注册表

1) HKEY_CLASSES_ROOT的子键CLSID 描述了每一个安装在系统上的组件
2) 重要的注册表主键项
项目名称    功能
ProgID    为COM类指定ProgID串,它不能包含多于39个字符,但是可以包含句号
InprocServer32   包含了32位DLL文件的路径和文件名称,它并不是一定需要包含路径.
    如果它的确没有包含文件路径,那么只有当它位于Windows的PATH指定
    的路径下时才可以被载入,16位版本里不包括扩展名"32".
VersionIndependentProgID 指定组件的最高版本
LocalServer32   包含了32位EXE文件的路径和文件名称
CurVer    组件类的最新版本的ProgID

2.5.8 组件的类别

Microsoft 提供了一种新的机制来描述组件的功能,被称为组件类别.它包括系统定义类别和用户定义类别

2.5.8.1 类别标识(CATID)

1)组件类别通过使用一个类别标识(CATID)来唯一地标定出来.
2)本地标识(locale ID)同CATID一起出现,该标识是通过一个十六进制的数字串和一个用户可读的字符串指定的.
3)已知的CATID存储在注册表的HKEY_CLASSES_ROOT\Componet Categories 键下.OLEVIEW工具可以根据组件的类别来显示组件

2.5.8.2 对组件进行分类

1)根据组件的功能分类.Implemented Categories 项列出这些组件所提供的功能类别
2)根据组件的可能客户所需要的功能分类.Required Categories项列出客户需要组件所提供的组件类别
3)Microsoft 定义了两个新的COM接口 - ICatRegister 和 ICatInfomation 支持组件类别
4)组件分类管理器实现了上面两个接口.它是系统级别的组件


2.5.8.3  组件分类管理器(Component CateGories Manager)

1) 组件分类管理器(CCM)是用来实现ICatRegister 和 ICatInfomation接口的一个简单的COM组件
2) 可用CoCreateInstance函数并传递由Microsoft定义的CCM CLSID:CLSID_StdComponentCategoriesMgr来建立一个CCM实例

2.5.8.4  ICatRegister

1) ICatRegister 接口提供了在系统级别和组件级别上注册和注销分类的方法
2) ICatRegister接口定义
interface ICatRegister : IUnknown
{
 HRESULT RegisterCategories(ULONG cCategories,CATEGORYINFO rgCategoryInfo[]);
 HRESULT UnRegisterCategories(ULONG cCategories,CATID rgcatid[]);
 HRESULT RegisterClassImplCategories(REFCLSID rclsid,ULONG cCategories,CATID rgcatid[]);
 HRESULT UnRegisterClassImplCategories(REFCLSID rclsid,ULONG cCategories,CATID rgcatid[]);
 HRESULT RegisterClassReqCategories(REFCLSID rclsid,ULONG cCategories,CATID rgcatid[]);
 HRESULT UnRegisterClassReqCategories(REFCLSID rclsid,ULONG cCategories,CATID rgcatid[]);
}

3)RegisterCategories方法把组件类别身份注册到系统上,即HKEY_CLASSES_ROOT\Component Categories键下
4)CATEGORYINFO结构
typedef struct tagCATEGORYINFO {
    CATID catid;
    LCID lcid;
    OLECHAR szDescription [128];
} CATEGORYINFO;

5)使用RegisterCategories方法
// include the component category interfaces and symbols
include "comcat.h"
...
HRESULT CreateComponentCategory(CATID catid,WCHAR*catDescription)
{
 ICatRegister *pcr = NULL;
 HRESULT hr = S_OK;
 // Create an instance of the category manager
 hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
   NULL,
   CLSCTX_INPROC_SERVER,
   IID_ICatRegister,
   (void **)&pcr);
 if (FAILED(hr))
  return hr;
  
 CATEGORYINFO catinfo;
 catinfo.catid = catid;
 
 // English locale id in hex
 catinfo.lcid = 0x0409;
 
 // Make sure the description isn't too large
 int len = wcslen(catDescription);
 if (len > 127)
  len = 127;
 wcsncpy(catinfo.szDescription,catDescription,len);
 catinfo.szDescription[len] = '\0';
 hr = pcr->RegisterCategories(1,&catinfo);
 
 pcr->Release();
 return hr;
}

6)使用RegisterClassImplCategories方法为某一组件添加\Implemented Categories 注册表项
ICatRegister *pcr = NULL;
HRESULT hr = S_OK;

// Create an instance of the category manager
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
   NULL,
   CLSCTX_INPROC_SERVER,
   IID_ICatRegister,
   (void **)&pcr);
if (SUCCEEDED(hr))
{
 // Register that we supper the "Control" category
 CATID rgcatid[1];
 rgcatid[0] = CATID_Control;
 hr = pcr->RegisterClassImplCategories(clsid,1,rgcatid);
}

if (pcr != NULL)
 pcr->Release();
 
7)同样,使用RegisterClassReqCategories方法给组件添加"Category Required"条目.代码同上


2.5.9 OLEVIEW

OLEVIEW 是Visual C++ 提供的一个工具程序,它是从COM的角度检查注册表的一个优秀工具.


2.5.10 组件的宿主文件

1)基于COM的组件要么被包容在一个可执行文件(EXE文件)里,要么被包容在一个Windows的动态链接库里.
2)基于COM的服务的三种不同的配置中实现
本地服务器(local server) --是一个可执行的组件宿主文件,它还支持COM组件之外的其他功能
进程内服务器(in-process server) --是Windows的一种DLL文件,它容纳了基于COM的组件
远程服务器(remote server)  --是在远程计算机上载入和执行的组件宿主文件
3)组件实现应考虑的主要因素:
性能 -- 进程内方式下组件性能最好,它是在客户进程的地址空间内执行的,不需要在方法参数之间进行进程间传递,但也有可能使客户应用程序崩溃
健壮性 -- 对于在进程外执行的组件,即使发生了重大的错误,也不会使客户应用程序崩溃


2.5.11 类工厂

1)COM提供了一个标准接口IClassFactory来实例化一个组件,它是一种与语言无关的方法,必须实现该接口组件才可以让外部客户可以创建自己的组件的实例.
2)IClassFactory接口
class IClassFactory : public IUnknown
{
 virtual HRESULT CreateInstance(LPUNKNOWN pUnk,REFID riid,void **ppv) = 0;
 virtual HRESULT LockServer(BOOL fLock) = 0;
}

CreateInstance -- 创建指定组件类的实例并返回该实例所请求的接口
LockServer -- 为客户提供了一个在内存里锁定一台服务器的途径

3) 类工厂的代码
MathClassFactory::MathClassFactory()
{
 m_lRef = 0;
}
MathClassFactory::~MathClassFactory()
{}

STDMETHODIMP_ MathClassFactory::QueryInterface(REFIID riid,void ** ppv )
{
 *ppv = 0;
 if (riid == IID_IUnknown || riid == IID_IClassFactory)
  *ppv = this;
 if (*ppv)
 {
  AddRef();
  return S_OK;
 }
 
 return (E_NOINTERFACE);
}

STDMETHODIMP_(ULONG) MathClassFactory::AddRef()
{
 return InterlockedIncrement(&m_lRef);
}

STDMETHODIMP_(ULONG) MathClassFactory::Release()
{
 if (InterlockedDecrement(&m_lRef) == 0)
 {
  delete this;
  return 0;
 }
 
 return m_lRef;
}

STDMETHODIMP_ MathClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,REFIID riid,void **ppvObj)
{
 Math *pMath;
 HRESULT hr;
 **ppvObj = 0;
 
 // Create our component instance
 pMath = new Math;
 if (pMath == 0)
  return (E_OUTOFMEMORY);
 
 hr = pMath->QueryInterface(riid,ppvObj);
 if (FAILED(hr))
  delete pMath;
 return hr;
}

STDMETHODIMP_ MathClassFactory::LockServer(BOOL fLock)
{
 if (fLock)
  InterlockedIncrement(&g_lLocks);
 else
  InterlockedDecrement(&g_lLocks);
 return S_OK;
}

4)CoGetClassObject函数获取某一组件类工厂的直接访问
int main()
{
 IClassFactory *pCF;
 IMath *pMath;
 HRESULT hr;
 
 // Get the class factory for the Math class
 hr = CoGetClassObject(CLSID_Math,
    CLSCTX_INPROC,
    NULL,
    IID_IClassFactory,
    &pCF);
 // using the  class factory interface create an instance of the
 // component and return  the IMath interface
 pCF->CreateInstance(NULL,IID_IMath,*pMath);
 
 // Release the class factory
 pCF->Release();
 
 // Use the component to do some work
 long lResult = pMath->Add(100,433);
 
 // Release it when we're finished
 pMath->Release();
}

 

2.6 基于COM的复用技术

描述:COM提供了两种二进制方式的复用:包容(containment)和集合(aggregation)

2.6.1 包容(Containment)

1)包容和组合分别使用了COM对象的服务功能或内部嵌入C++类来实现代码的复用.被包容的组件接口只能以间接的方式展示给外部的用户这种间接的方法是通过包容(或外部)组件所提供的方法完成的.
2)同C++,内部对象的生存期完全受外部组件的控制.


2.6.2 集合(Aggrgation)

1)集合与包容的差别:在集合里,内部COM对象的接口可以直接被用户使用,外部集合对象的IUnknown接口提供了对所有内部对象接口的访问
2)对外部对象和内部对象生存期的管理必须通过IUnknown的实现来进行协调


2.7 COM的API函数

描述:Microsoft提供了一些COM,ActiveX和OLE专用的Win32 API函数.下面列举了一些:
   基本的 COM 函数
函数名称     功能
CoInitialize,CoInitializeEx  初始化COM库以供进程使用
CoUninitialize(client and server) 释放COM库,在进程内服务器上不能使用该函数
CoGetClassObject(client)  为一个指定的COM对象获取一个类工厂实例
CoCreateGUID(client and server)  创建一个新的,特有的GUID
CoCreateInstance(client)  创建一个特定COM对象的实例,它可以位于远程计算机上
CoRegisterClass(server)   为一个特定COM对象的类工厂进行注册,表明它的存在
DllCanUnloadNow(in-process server) COM将定时调用它,用以决定DLL是否可以被下载,该函数又进程内服务器来实现
DllGetClassObject(server)  入口点由进程内服务器实现,从而客户进程可以获得它的类工厂接口


2.8 其他涉及COM的问题

2.8.1 C++ 里关于COM的宏:STDMETHOD 和 STDMETHODIMP

1)COM接口的声明和定义中可以使用四种宏
STDMETHOD,STDMETHODIMP  -- 表明从某一方法中返回的HRESULT
STDMETHOD_,STDMETHODIMP_ -- 只能和IUnknown的AddRef和Release一起使用
2)除了两种情况以外,每一个COM的接口方法都应该返回一个HRESULT.
class IMath
{
public:
 virtual long Add(long,long) = 0;
 virtual long Subtract(long,long) = 0;
 virtual long Multiply(long,long) = 0;
 virtual long Divide(long ,long) = 0;
}

接口可以按照如下方法声明,使用COM的宏并且像指针一样把返回值移动到尾部
class IMath:public IUnknown
{
public:
 STDMETHOD(ADD)(long,long,long *) PURE;
 STDMETHOD(Subtract)(long,long,long*) PURE;
 STDMETHOD(Multiply)(long,long,long*) PURE;
 STDMETHOD(Divide)(long,long,long*) PURE;
};
3)STDMETHOD的实际展开情况和目标平台的类型,以及你究竟是使用C还是C++有关

// C++,Win32 下 OBJBASE.H
#define STDMETHODCALLTYPE __stdcall
...
#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method
#define STDMETHOD_(type,method) virtual type STDMETHODCALLTYPE method
#define PURE   = 0
#define STDMETHODIMP  HRESULT STDMETHODCALLTYPE
#define STDMETHODIMP_(type) type STDMETHODCALLTYPE

其中__stdcall 是Microsoft特有的函数调用约定,它要求主函数在调用完毕后应该清理堆栈

4)STDMETHOD宏也是用在所实现类的接口方法的声明里,特别之处在于你不必使用PURE说明符
class Math:public IMath,public IAdvancedMath
{
 // IMath
 STDMETHOD(Add)(long,long,long*);
 STDMETHOD(Subtract)(long,long,long*);
 STDMETHOD(Multiply)(long,long,long*);
 STDMETHOD(Divide)(long,long,long*);
};

5)STDMETHODIMP宏用在接口函数的实现里(.cpp)
STDMETHODIMP Math::Add(long Op1,long Op2,long *pResult)
{
 *pResult = Op1 + Op2;
 return S_OK;
}

STDMETHODIMP Math::Subtract(long lOp1,long lOp2,long *pResult)
{
 *pReuslt = lOp1 - lOp2;
 return S_OK;
}

2.8.2 COM和Unicode

1)所有COM函数和标准接口方法都需要使用Unicode字符串.
2)当我们把字符串传递给COM函数或让它们通过一个COM接口方法时,就必须把我们的字符串从ANSI型转变为Unicode型.有以下几种方法

方法一:在字符串的前面加上一个"L"
//Get the unique CLSID from the ProgID
HRESULT hr = ::CLSIDFromProgID(L"Chapter2.Math.1",&clsid);

方法二:使用自带的Win32 API函数
// conver the ProgID to Unicode
char *szProgID = "Chapter2.Math.1";
WCHAR szWideProgID[128];
CLSID clsid;
long lLen = MultiByteToWideChar(CP_ACP,
    0,
    szProgID,
    strlen(szProgID),
    szWideProgID,
    sizeof(szWideProgID));
// Teminate the returned string
szWideProgID[lLen] = '\0';

方法三:使用MFC或ATL提供的宏
// provide temp variable
USES_CONVERSION;

// Get the unique CLSID from the ProgID
char * szProgID = "Chapter2.Math.1";
HRESULT hr = ::CLSIDFromProgID(A2W(szProgID),&clsid); // 相应的W2A

2.8.3 COM和多态性

1)多态性是指一个对象对同一个消息(C++中)可以有不同的回应方式

int main()
{
 Fruit* pFruitList[4];
 pFruitList[0] = new Apple;
 pFruitList[1] = new Oranage;
 pFruitList[2] = new Grape;
 pFruitList[3] = new GrannySmith;
 for (int i = 0; i < 4; i ++)
  pFruitList[i]->Draw();
}


class IFruit
{
 virtual void Draw() = 0;
 ... ...
};

IFruit* pFruit[4];
CoCreateInstance(CLSID_Apple,IID_IFruit,...,(void **)&(pFruit[0]));
CoCreateInstance(CLSID_Orange,IID_IFruit,...,(void **)&(pFruit[1]));
CoCreateInstance(CLSID_Grape,IID_IFruit,...,(void **)&(pFruit[1]));
CoCreateInstance(CLSID_GrannySmith,IID_IFruit,...,(void **)&(pFruit[1]));
for (int i = 0; i < 4; i ++)
 pFruit[i]->Draw();

分享到:
评论

相关推荐

    ATL学习笔记.doc

    6. 从向导中选择“ATL” -&gt; “ATL简单对象” -&gt; “添加”。 7. 在ATL简单对象向导中,输入类的简称,如“FIRSTclass”,并自定义ProgID(例如:FirstCOM.math)。点击“完成”。 8. 在类视图中,选择新添加的接口...

    windows shell、atl、https学习笔记

    总的来说,这个学习笔记涵盖了Windows系统核心组件、C++组件开发技术以及网络通信安全的关键概念,是一份全面的IT技术学习资料。通过深入学习这些内容,你可以提升自己在Windows平台上的软件开发和网络安全能力。

    ATL——com学习笔记

    在ATL中,默认情况下,每个类都会有一个自动创建的类工厂。然而,在某些情况下,我们可能希望替换这个默认的类厂,以实现更加灵活或者自定义的功能。 #### 实现步骤 1. **定义自己的类工厂**:首先需要继承自`...

    com学习笔记

    在COM学习笔记中,主要涉及的是COM线程管理的概念,包括线程单元(Thread Apartments,简称TA)以及线程如何与COM对象交互。 线程单元是COM中管理线程与对象交互的核心机制。它是一个逻辑上的容器,确保同一单元内...

    Visual C++程序设计学习笔记(1-12 源码)

    本资源包含的是Visual C++程序设计的学习笔记,涵盖了从基础到进阶的12个章节的源代码,对于学习和理解C++编程,特别是使用Visual C++环境编程,具有极高的参考价值。 首先,我们来详细探讨一下Visual C++的关键...

    ATL_OFFICE_COM

    4. **新建 文本文档 (3).txt**: 这可能是一个文本文件,可能包含了代码片段、笔记或其他辅助信息。 通过学习这些资源,开发者可以深入了解ATL和Office COM插件的开发,从而创建出自己的定制化Word插件,提高办公...

    VC学习笔记及项目代码1

    【VC学习笔记及项目代码1】是一份涵盖了多个VC++编程关键领域的学习资源,包括了Win32 API、MFC(面向对象的)框架、COM(Component Object Model,组件对象模型)及其ATL(Active Template Library,活动模板库)...

    COM学习笔记和测试程序

    本学习笔记和测试程序旨在帮助你深入理解COM的工作原理和实践应用。 1. **COM基础概念** - **组件**: 在COM中,组件是实现了COM接口的二进制模块,可以是DLL或EXE。 - **接口**: 接口定义了组件对外提供的服务,...

    vc++技术内幕第四版(清华版)学习笔记

    《VC++技术内幕第四版(清华版)》是C++编程领域的一本经典著作,尤其对于深入理解Microsoft的Visual C++编译器和MFC框架有...这份学习笔记则将书中的精华内容进行了整理,便于读者查阅和复习,是一份宝贵的参考资料。

    MFC学习笔记加windows32

    1. MFC架构:MFC由几个关键部分组成,包括框架类、文档/视图结构、对话框、控件、GDI(图形设备接口)以及ATL(Active Template Library)等。其中,框架类如CWinApp、CDocument、CView等,提供了应用程序的基本结构...

    Matlab学习笔记#01

    在一直以来的学习当中,与matlab这款软件多有接触,但一直没有系统化地研究学习一下这款软件,趁着闲暇之余,整理一下自己有关matlab知识。 matlab技术支持搜索网站 一些基本的符号 ans→answer inf→∞ eps→2.2204...

    EMF 自学笔记5

    ### EMF自学笔记知识点详述 #### 一、EMF框架概述 EMF(Eclipse Modeling Framework)是Eclipse项目中的一个子项目,它提供了一套用于构建模型驱动的开发工具和应用程序的框架。EMF的主要目标是简化元数据驱动应用...

    学习mfc的视频笔记

    这篇“学习MFC的视频笔记”显然是针对想要了解或深入学习MFC的初学者设计的,提供了视频教程的笔记内容,旨在帮助初学者快速入门。 1. **MFC基础**:MFC的核心是它的框架,它包括了各种C++类,如CWinApp、CWnd、...

    个人整理的资料

    "MyLearn.chm"可能包含了作者的学习笔记、教程或项目案例,涵盖了作者在VC++学习过程中的各种理解和感悟。"STL.chm"则很可能是关于标准模板库(Standard Template Library,STL)的详细解释,STL是C++编程中不可或缺...

    matlab学习笔记

    这篇学习笔记主要涉及了几个关键概念,包括标量、矩阵的迹、矩阵维度的获取、单位矩阵的生成、决策变量的定义以及几个相关的函数应用。 1. **标量(Scalar)**:在数学和计算机科学中,标量是一个单一的数值,不...

    17--锂离子电池工艺流程介绍(ATL教材).pdf

    本文档《17--锂离子电池工艺流程介绍(ATL教材).pdf》详细介绍了锂离子电池的整个生产过程,涵盖了从材料选择到最终成品测试的各个阶段,为锂电行业提供了一份宝贵的工艺教材。 锂离子电池的生产工艺流程可以划分...

    YearUp:在 Year Up ATL 创建的文件的存储库

    8. **学习资源**:除了代码,"YearUp-master"可能还包括学习笔记、教程链接、问题解答等辅助学习资料。 9. **团队协作**:Year Up的项目可能涉及多人合作,因此可能包含关于团队分工、沟通和协作的最佳实践。 10. ...

    matlab学习笔记:如何在matlab中如何自定义函数和匿名函数

    matlab的程序语言在拓展性上没有C++齐全,但也足够应付一些简单的程序,而且比C++更易于学习。 input():数据的输入函数。 disp():数据的输出函数。 pause(延迟时间):延迟函数。 其余的程序语句和C++大同小异,当然...

Global site tag (gtag.js) - Google Analytics