“COM库”的意思是,为了支持COM功能实现,需要这个COM库,它在COM 客户,服务器之外扮演第三个角色。其实可以理解为一套DLL。COM基本功能的实现经常会需要调用COM库的引出函数。
一 类厂的概念
类厂是COM对象的生产基地,COM库通过类厂创建COM对象。所以确切的讲,类厂应该称为“对象厂”。类厂本身也是一个COM对象,支持一个特殊的接口IClassFactory。 厂其c++表示如下:
class IClassFactory: public IUnknown
{
virtual HRESULT _stdcall CreateInstance
( IUnknown * pUnknownOuter, /*IUnknown * pUnknownOuter 参数只用于对象类聚和的情况,其他时候都为NULL*/
const IID&iid, /*const IID& iid 参数为对象创建成功后传递给客户的初始接口的IId*/
void **ppv /*void ** ppv存放返回的接口指针。 */
)=0 ;
virtual HRESULT _stdcall LockServer (BOOL bLock)=0 ; /*成员函数LockServer()用于控制组建的生存周期*/
//
}
对应于每一个com类都应该用一个类厂专门来对该类的对象进行创建。 这个创建操作就通过其成员函数 CreateInstance完成。 因为每个类场只对应一个特定的COM进行创建对象,所以类厂CreateInstance函数会知道该如何创建。该函数各个参数的含义如标注所示。
类厂创建对应的COM类的对象;而COM组件中的类厂是由 HRESULT DllGetClassObject() 引出函数创建。请注意,这个引出函数是定义在服务器端的组件dll中。而这个函数对于客户来说是透明的,也就是说,客户不会直接调用这个函数。
HRESULT DllGetClassObject
(
const CLSID& clsid,
/* const CLSID& clsid是待创建对象的CLSID*/
const IID& iid,
/*const IID& iid表示指定接口IID;*/
(void **) ppv
/*void ** ppv 存放类厂接口指针。*/
) ;
标准COM库在接到对象创建的指令后,调用进程内组件的DllGetClassObject函数创建类厂对象,并返回类厂对象的借口指针。 然后客户就可以通过类厂对象的CreateInstance()函数和篡改相应的COM对象。下面详细介绍这个过程。
二 COM库与类厂的交互
类厂是由函数:DllGetClassObject()函数创建。而类厂的创建过程对客户程序来说是透明的,也就是说客户并不知道类厂的存在。
在标准COM库中,有三个API函数可以用于COM对象的创建:CoGetClassObject(), CoCreateInstance(), CoCreateInstanceEx()。客户可以调用其中之一来创建对象并获得新对象的初始接口指针。而COM库与类厂也通过这三个函数进行交互。其实在这些函数中,调用了服务器端组件的DllGetClassObject()函数来创建了类厂对象。分别介绍这三个函数。
1 HRESULT CoGetClassObject
(
const CLSID& clsid,
DWORD dwClsContext, /*指定组件类型:进程内,进程外,进程内控制对象(类似于进程外组件的代理对象,用于ole技术)*/
COSERVERINFO * pServerInfo, /*在创建远程对象时指定服务器信息,其他时候设置为NULL*/
const IID& iid, /*对应DllGetClassObject() 函数的 iid参数,指定接口IID 。通常为IClassFactory的接口标志符
IID_IClassFactory,也可以为IClassFactory2的接口标志符。*/
(void **)ppv /*对应DllGetClassObject() 函数的 ppv参数,存放类厂对象的接口指针*/
);
如果COM对象是进程内组件对象则 CoGetClassObject()调用DLL模块的DllGetClassObject引出函数来创建类厂,并返回类厂对象接口指针。
如果CoGetClassObject()函数创建的类厂对象位于进程外组件,则进行一下步骤:(1)CoGetClassObject()函数启动组件进程,(2)等待组件进程把它支持的COM类对象的类厂通过函数CoRegisterClassObject()注册到COM中,以便COM库创建COM 对象使用 (3)CoGetClassObject函数把COM相应的类厂信息返回 (4)当组件进程退出时,调用CoRevokeClassObject()函数使得类厂对象不再有效。组件程序调用CoRegisterClassObject()和CoRevokeObject()必须成对调用。
2 不过在在通常的com对象创建过程中,客户端不会直接调用CoGetClassObject() 函数创建并获得类厂对象并获得接口指针,而是调用函数CoCreateInstance()。这样客户可以通过一次调用就获得最终的COM对象,类厂的创建对客户是完全透明。函数CoCreateInstance定义如下以及实现如下:
HRESULT CoCreateInstance
(
const CLSID& clsid,
DWORD dwClsContext, /*指定创建类型:进程内,进程外,进程内控制对象*/
const IID& iid, /*对应DllGetClassObject() 函数的 iid参数,指定接口IID 。通常为IClassFactory的接口标志符
IID_IClassFactory,也可以为IClassFactory2的接口标志符。*/
(void **) ppv /*对应DllGetClassObject() 函数的 ppv参数,存放类厂对象的接口指针*/
)
{
IClassFactory *pCF;
HRESULT hr;
hr=CoGetClassObject(clsid, dwClsContext, NULL, IID_IClassFactory, (void *)pCF) ;
/*首先创建类厂对象,并获得类厂对象接口指针。注意 COSERVERINFO * pServerInfo参数为NULL,因此无法这个函数不适用于创建远程机器上的对象*/
if (FAILD(hr))
return hr;
hr=pCF->CreateInstance(pUnkOuter, iid, (void *)ppv) ;
/*由获得的类厂对象接口指针创建对应COM对象实例*/
pCF->Release() ; //注意这句,类厂对象在创建完自己的COM对象就丢弃了。
return hr;
}
这个函数的功能就是把类厂创建对象的过程封装起来。这样客户只要指定对象类的CLSID和要输出的接口指针及接口ID,函数返回后客户就可以得到对象的接口指针。
3因为函数CoCreateInstance()无法创建远程机器上的COM对象,因此还需要另一个函数:CoCreateInstanceEx()。这个函数唯一特别的地方在于其使用了PSERVERINFO * pServerInfo参数。其函数声明如下:
HRESULT CoCreateInstanceEx
(
const CLSID& clsid,
IUnknown* pUnknownOuter, /*适合聚合,一般为NULL*/
DWORD dwClsContext, /*创建的对象类型:进程内,进程外,进程内控制对象*/
COSERVERINFO * pServerInfo, /*创建远程机器上对象时指定远程机器信息*/
DWORD dwCount,
MULTI_QI* rgMultiQI
) ;
参数pServerInfo指定服务器信息, DWORD dwCount 与参数 MULTI_QI* rgMultiQi 指定了一个结构数组,可以保存多个对象接口指针,这样可以一次获得多个接口指针,节省网络交互。
一般来说, 如果创建远程对象或者希望一次获取对象的多个接口,则选用CoCreateInstanceEx()API函数; 如果希望获取类厂对象或者调用类厂的成员函数则选用CoGetClassObject()API函数; 其他就是最常用的情况,用CoCreateInstance()API函数来创建COM对象