之前文章都是使用COM SDK来开发COM,实际中,因为COM开发涉及到各种重复、繁琐的细节,微软在两个C++类库MFC和ATL中实现了对COM开发的支持。MFC和ATL大大减少了COM的开发工作量,本文讲MFC开发简单的COM,下文讲ATL开发简单的COM。
1.建立COM工程
建立一个MFC工程,选择DLL,如下勾选[自动化]
点击完成,查看生成的代码,发现MFC框架已经帮我们做的事情有
1.实现如下四个标准的COM加载和导出函数
DllCanUnloadNow
DllGetClassObject
DllRegisterServer
DllUnregisterServer
2.包含一个.idl命名的文件,可以通过编写IDL实现代码自动生成,在这里还是讲手动生成MFC代码,下一节ATL讲通过编写IDL实现COM
2.实现COM
本文实现如下COM,ICat和IDog接口分别实现自己的方法SayHello.
a.嵌套类原理
和之前使用C++虚函数实现COM原理不同,MFC对COM的实现,基于嵌套类,简单示例如下:
class CAnimalObj
{
public:
class XCat
{
public:
AddRef
Release
QueryInterface
SayHello
} m_xlocalCat;
class XDog
{
public:
AddRef
Release
QueryInterface
SayHello
} m_xlocalDog;
}
不同的接口实现,都是一个嵌套类,其实基本思想和虚函数机制一样,需要注意的是AddRef/Release/QueryInterface均在父类CAnimalObject中实现,嵌套的接口类调用CAnimalObject的实现,这也说明COM接口只是提供接口服务,而COM对象才保持对象的状态,进一步演示请参看《COM原理与应用》。
b.CCmdTarget实现接口查询和生命周期管理
之前,我们的接口查询和生命周期管理都是自己来做的,在MFC中已经在基类CCmdTarget中帮我们实现了AddRef/Release/QueryInterface,同时也支持聚合,只要在类构造函数中调用EnableAggregation即可开启聚合,具体实现原理和之前讲的差不多。
嵌套类实现如下,
定义嵌套类:
//嵌套类实现接口ICat
BEGIN_INTERFACE_PART(Cat, ICat)
INIT_INTERFACE_PART(CAnimalObj, Cat)
STDMETHOD_(void, SayHello)(LPCWSTR szWord);
END_INTERFACE_PART_STATIC(Cat)
//嵌套类实现接口IDog
BEGIN_INTERFACE_PART(Dog, IDog)
INIT_INTERFACE_PART(CAnimalObj, Dog)
STDMETHOD_(void, SayHello)(LPCWSTR szWord);
END_INTERFACE_PART_STATIC(Dog)
BEGIN_INTERFACE_PART包含了AddRef/Release/QueryInterface定义,
INIT_INTERFACE_PART定义了一个成员变量m_nOffset,表示当前嵌套类虚表和CAnimalObject虚表的偏移,这样在嵌套接口中很容易访问到AnimalObjet成员,
END_INTERFACE_PART定义了成员变量m_xlocal...,对应于对应的嵌套类
具体的AddRef/Release/QueryInterface实现:
STDMETHODIMP_(ULONG) CAnimalObj::XCat::AddRef()
{
METHOD_PROLOGUE_EX_(CAnimalObj, Cat)
return pThis->ExternalAddRef();
}
STDMETHODIMP_(ULONG) CAnimalObj::XCat::Release()
{
METHOD_PROLOGUE_EX_(CAnimalObj, Cat)
return pThis->ExternalRelease();
}
STDMETHODIMP_(HRESULT) CAnimalObj::XCat::QueryInterface(REFIID iid, LPVOID* ppvObj)
{
METHOD_PROLOGUE_EX_(CAnimalObj, Cat)
return pThis->ExternalQueryInterface(&iid, ppvObj);
}
这里METHOD_PROLOGUE_EX_根据当前嵌套类虚表和CAnimalObject虚表的偏移计算出CAnimalObject的虚表,调用对应的函数。
那么这里父类的QueryInterface怎么实现呢,答案是MFC经典套路——查表,这里又叫接口映射表,如果对这一套查表宏机制实现不熟悉的话,建议去看下侯捷的《深入浅出MFC》中MFC六大关键技术仿真一章。
如下,在头文件中定义接口表
//接口映射表定义
DECLARE_INTERFACE_MAP()
在cpp文件中,创建表如下
//接口查询表
BEGIN_INTERFACE_MAP(CAnimalObj, CCmdTarget)
INTERFACE_PART(CAnimalObj, IID_ICat, Cat)
INTERFACE_PART(CAnimalObj, IID_IDog, Dog)
END_INTERFACE_MAP()
表的每一项是嵌套类虚表和CAnimalObject虚表的偏移大小,实际CCmdTraget中实现QueryInterface时查询该表,返回对应接口的虚表入口地址。
c.工厂类的实现
在前面实现COM对象时,每一个COM对象都有对应的工厂类,在MFC中实现了一个通用的工厂类COleObjectFactory,他实现了CreateInstance和LockServer,只需要传入指定参数接口,这个工厂类需要MFC类的动态创建功能。
在头文件中定义动态创建支持和工厂类定义
//支持动态创建
DECLARE_DYNCREATE(CAnimalObj)
//引入本组件ClassFactory
DECLARE_OLECREATE(CAnimalObj)
在cpp文件中,实现动态创建和工厂类
//支持动态创建
IMPLEMENT_DYNCREATE(CAnimalObj, CCmdTarget)
//定义本组件ClassFactory
// {49DC8329-D2EB-440c-BCF6-AA9CAA583DE1}
IMPLEMENT_OLECREATE(CAnimalObj, "CAnimalObj.Object",
0x4DDDE51A, 0x2252, 0x4EFC, 0x94, 0x54, 0xC5, 0x74, 0xEB, 0xC4, 0x14, 0x8A);
需要传入对应组件对象的ProgID和Clsid。
这里实际的工作过程在于COleObjectFactory的构造函数中,将当前的ProgID和Clsid信息传入到了MFC AFC_MODULE_STATE状态接口中类厂表中保存,当想COM组件请求对应组件对象的接口时,会在类厂表中找到对应的类厂创建出组件对象。
d.接口实现
//接口实现
STDMETHODIMP_(void) CAnimalObj::XCat::SayHello(LPCWSTR szWord)
{
wcout << L"喵~ 猫大王发话: " << szWord << endl;
}
STDMETHODIMP_(void) CAnimalObj::XDog::SayHello(LPCWSTR szWord)
{
wcout << L"汪~ 狗大王发话: " << szWord << endl;
}
到此,一个简单的MFC COM组件就实现了。本文中我们都是手动添加代码,之前我们有提自动生成一个类型库.idl文件,实际上我们可以编写.idl让MIDL编译自动生成对应代码,这个在下一节ATL实现COM中会讲到。
本文完整演示代码下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219
来源:CSDN
作者:文大侠
链接:https://blog.csdn.net/wenzhou1219/article/details/51924221