2009-1-16 下午14:13
Pearson Bee
大学时就已经接触了COM,但一直到工作后才有机会将COM理论运用于项目。现在,回归本质,谈谈如何在客户端创建COM组件并返回组件上所支持的接口指针。
我们知道,客户是通过调用COM库函数CoCreateInstance来创建组件并返回接口指针的,但实际上,这个函数首先创建了与所需创建组件相对应的组件,即类厂,再由类厂接口创建‘真正’的组建。在这里,类厂接口是一个派生自IUnknown的借口IClassFactory,它定义了两个纯虚函数CreateInstance和LockServer,其中,用户可以实现CreateInstance,以创建‘真正’的组建。
下面,就用C++来描述整个过程:
一.首先看看CoCreateInstance的实现:
HRESULT CoCreateInstance(const CLSID& clsid,//所要创建的组建的GUID
IUnknown *pUnknownOuter,//置零,聚合时用到
DWORD dwClsContext,//组件执行上下文
const IID& iid,//接口的GUID
void **ppv //输出变量,接收返回的接口指针
)
{
*ppv=NULL;
IclassFactory *pIFactory=NULL;
HRESULT hr=CoGetClassObject(clsid,
dwClsContext,
NULL,
IID_IclassFactory,
(void **)&pIFactory
);
If(SUCCEEDED(hr))
{
pIFactory->CreateInstance(NULL,iid,(void **)&ppv);
pIFactory->Release();
}
return hr
}
在用户的这个函数里,将真正创建类厂的任务交给了库函数CoGetClassObject,函数CoGetClassObject根据clsid创建了相应的类厂,返回接口指针,赋值pIFactory。之后,就可以使用类厂接口方法CreateInstance创建组件了。那么CoGetClassObject又是如何实现的呢?请看二。
二.CoGetClassObject的实现:
库函数CoGetClassObject实际上调用了封装在dll服务器里的导出函数DllGetClassObject,在DllGetClassObject函数里,我们将会看到类厂的创建过程,在这里使用new操作符生成了一个类厂对象,并使用QueryInterface返回类厂接口IclassFactory的指针。DllGetClassObject的实现如下:
STDAPI DllGetClassObject(const CLSID& clsid,
const IID& iid,
void **ppv
)
{
*ppv=NULL;//先置零,这是好习惯
if(clsid!=CLSID_MyComponet)//前面所提到的‘与所要创建组件相对应的
{ //类厂’,就表现在这个判断语句里
return CLASS_E_NOTAVAILABLE;
}
HRESULT hr=CFactory *pFactory=new CFactory;//new出一个类厂
pFactory->QueryInterface(iid,(void **)&ppv);//返回类厂接口指针
pFactory->Release();
return hr;
}
那么,CoGetClassObject又是如何调用DllGetClassObject的?方法有两种,一是显示调用(LoadLibrary+GetProcessAddress),二是隐式调用(#progma+def),至于这两个方法的实现细节,这里不讲了。
在第一步中,我们看到,CoCreateInstanc函数里面通过调用所返回类厂接口的方法CreateInstance来创建’真正’的组件并返回组件接口指针的,即pIFactory->CreateInstance(NULL,iid,(void *)&ppv);那么这个方法又是如何实现的呢?请看三:
三.一般地,我们在实现自己组建的同时,也实现了一个类厂CFactory(它本身也是一个COM组件,因为它所支持的接口IClassFactory派生自IUnknown。在这里将定我们自己的组建名叫Cmpt,类厂名叫CFactory)。
首先看看IClassFactory的定义:
interface IClassFactory:public IUnknown
{
virtual HRESULT __stdcall CreateInstance(IUnknown *pUnknownOuter,
const IID& iid,
void **ppv)=0;
virtual HRESULT __stdcall LockServer(BOOL bLock)=0;
}
CreateInstance的实现:
class CFactory:public IClassFactory
{
virtual HRESULT __stdcall QueryInterface(REFIID riid,void **ppv);
virtual ULONG __stdcall AddRef(void);
virtual ULONG __stdcall Release(void);
virtual HRESULT __stdcall CreateInstance(IUnknown *pUnknownOuter,
const IID& iid,
void **ppv);
virtual HRESULT __stdcall LockServer(BOOL bLock);
}
HRESULT __stdcall CFactory::CreateInstance((IUnknown *pUnknownOuter,
const IID& iid,
void **ppv)
{
*ppv=NULL;
if(pUnknownOuter!=NULL)//必须置NULL
{
return CLASS_E_NOAGGREGATION;
}
Cmpt *pCmpt=new Cmpt;//在这里,才真正的创建了组件
HRESULT hr=pCmpt->QueryInterface(iid,(void **)ppv);//返回组件所支持
pCmpt->Release(); //的接口指针
return hf;
}
当然,对组件用户来说,没有必要了解组件的创建过程的(事实上,了解它总不是件坏事^_^),可是,对于组件的开发人员来说,那就需要深入了解它了。比如开发DirectShow应用程序,Filter用户没必要了解上述过程,但开发Filter的人员就必须对其内部机制有个清楚的理解。