一个简单COM的应用开发
实例中为一个动态链接库文件,需要导出一系列函数:
extern “C” HRESULT __stdcall DllCanUnloadNow(void)
extern “C” HRESULT __stdcallDllRegisterServer()
extern “C” HRESULT __stdcallDllUnRegisterServer()
extern “C” HRESULT __stdcallDllGetObject(const CLSID &,const IID &,void **)
COM服务端开发
流程如下:
1) 分配一个对象签名标识符CLSID,提供组件模块与CLSID之间的映射
2) 对于每个接口CLSID,均需实现一个类工厂(继承自IClassFactory接口),用于组件的内存分配及接口指针的路由(CreateInstance)
3) 在类工厂中暴露实现的接口以便于COM服务器在装载dll后能及时定位到对应的接口
4) 当没有对象引用或调对象无锁时,调用类工厂卸载该COM模块。(LockServer)
5) 开发具体业务接口(继承自IUnknown接口),在开发过程中只能使用上述内存分配函数
6) 进行注册表的注册,将接口参数写入注册表
7) COM为一个dll,需要提供处理导出函数
8) 调用命令regsvr32来注册dll形式的COM
Windows中CLSID保存在注册表中(Win7中32/64位COM注册表分开保存至不同的路径),如:
CLSID
{54BF6567-1007-11D1-B0AA-444553540000}= Dictionary Component,在该键值下面又有其它一些项:
服务项 | 子键名称 | 含义 |
同一进程 | InprocServer32 | COM组件dll形式的路径名 |
本地机器的不同进程 | LocalServer32 | COM组件exe形式的路径名 |
同一进程对象句柄 | InprocHandler32 | 将服务端的对象在客户端中重建但又不完全本地化,主要用于性能优化 |
COM 客户端开发:
COM客户端开发比较简单,包含COM服务端的接口,通过调用系统提供的API来获取接口指针,并通过QueryInterface函数来实现接口之间的跳转。步骤如下:
1. 初始化COM库,启动模式
2. 调用CoCreateInstance,QueryInterface获取接口指针
3. 调用接口方法
4. 卸载COM库
COM的调用流程
实例调用流程如下:
1. 客户端CoCreateInstance创建指定CLSID组件
2. 系统通过查找注册表找到指定COM dll,并调用LoadLibrary装载至内存,调用导出函数DllGetObject获取接口指针
3. DllGetObject方法创建类厂并返回至CoGetClassObject,继续返回至CoCreateInstance函数
4. CoCreateInstance函数得到类厂后,调用类厂对象创建函数
5. 类厂创建COM对象,向CoCreateInstance返回对象指针
6. 客户端获取对象指针,访问接口方法CoCreateInstance方法源码如下(来自COM标准文档):
HRESULT CoCreateInstance(REFCLSID clsid, IUnknown * pUnkOuter,
DWORD grfContext, REFIID iid, void * ppvObj)
{
IClassFactory * pCF;
HRESULT hr;
hr=CoGetClassObject(clsid, grfContext, NULL, IID_IClassFactory, (void *)pCF);
if (FAILED(hr))
return hr;
hr=pCF->CreateInstance(pUnkOuter, iid, (void *)ppv);
pCF->Release();
/*
* If CreateInstance fails, ppv will be set to NULL. Otherwise
* ppv has the interface pointer and hr contains NOERROR.
*/
return hr;
}
COM调试工具
由于实例为dll,这里采用的工具为DumpBin来查看dll导出符号是否成功(VS2010命令行启动dumpbin /EXPORTS dll路径名)
通过设置命令行(vs2010)进行断点调试:
项目->DictComp 属性页 -> 调试 -> 命令:Regsvr32;命令参数: /u $(TargetPath)
具体源码见 这里