COM客户程序如何调用外组件
COM挂接一个独立的EXE组件可是比挂接一个DLL组件要复杂的多。EXE组件不在同一的进程内,并且可能不在同一个机器内,不过,不用担心。尽管把它当作DLL来写你的程序。COM通过它的远程构架来处理这些细节,这个远程控制访问的结构体系通常已经包含了Remote Procedure Call(RPC)。
在RPC中,客户程序会调用一个特殊的DLL即代理(proxy), proxy发送数据流给残根(stub),stub在组件进程的一个DLL中。当客户程序调用一个组件方法,proxy发送一条信息给组件程序来通知stub,这些是在隐藏的windows窗口内进行的。转换接受和发送的数据参数的这种机制叫做marshal(装配)。
如果你使用标准的接口(这些接口已经被Microsoft定义),如IClassFactory和IPersist(这个接口我们还没有看到过,不过如果你查一下COM的固化(persistence)你将会看到),proxy和stub的代码是用来执行marshal的,它有windows的OLE32 DLL提供。如果你创造一种你自己的接口,比如IMotion和IVisual,那么你将需要编写你自己的proxy和stub。编写proxy和stub类,其中包含用IDL(Interface Definition Language)定义你自己的接口,以及编译这些代码,幸运的是,这些已经被MIDL(Microsoft Interface Definition Language)编译器实现了。
下面是EXE客户程序与EXE组件交互的伪代码,把它和上面的DLL版本作个比较,注意客户端的调用是完全一致的。
Client
CLSID clsid;
IClassFactory* pClf;
IUnknown* pUnk;
CoInitialize(NULL);//Initialize COM
CLSIDFromProgID("componentname", &clsid);
COM
COM通过注册表寻找"componentname"的类ID
Client
CoGetClassObject(clsid, CLSCTX_LOCAL_SERVER, NULL, IID_IClassFactory, (void**)&pClf);
COM
COM用类ID在内存中寻找该组件
if(如果EXE组件没有被载入,或者我们需要另外一个实例){
COM从注册表中取得EXE组件的文件名
COM载入EXE组件
}
EXE Component
if(已经被载入){
全局factory对象被构建
InitInstance被调用(仅仅针对MFC)
CoInitialize(NULL);
for each factory对象{
CoRegisterClassObject(...);
Return IClassFactory* 指针给COM
}
}
COM
COM返回请求的接口指针给客户端程序
(客户端的指针与组件接口的指针并不相同)
Client
pClf->CreateInstance(NULL, IID_IUnknown, (void**)&pUnk);
EXE Component
class factory的CreateInstance方法被调用
(通过marshal转换直接访问调用)
构建"componentname"类对象
间接返回请求的接口指针
Client
pClf->Release();
pUnk->Release();
EXE Component
间接接调用"componentname"的释放函数
if (引用计数 == 0){
对象进行自我销毁
}
if(所有的对象被释放){
组件自行退出
}
Client
CoUninitialize();//准备退出
COM
COM调用Release用来释放所有客户程序没有释放掉的对象
EXE Component
组件退出
COM
COM释放所有资源
Client
客户程序退出
下一节:The MFC Interface Macros