使用MFC开发COM依赖MFC庞大的类库,开发的基于COM技术的ActiveX控件不利于在Web传输,因此微软推出了新的解决方案——ATL(Active Template Library),和基于简单封装和继承原理的MFC不同,ATL充分利用了C++泛型编程,开发的程序十分短小精悍。
本文主要讲在ATL中如何通过IDL编写COM,主要是为了讲在ATL中COM编写框架,了解整个结构,可以先参考实现,具体涉及的IDL、多线程、代理/存根等概念之后都会讲解。
1.建立工程
建立一个ATL工程,所有都是默认设置。
可以看到,ATL框架已经帮我们做的事情有
1.实现如下四个标准的COM加载和导出函数
DllCanUnloadNow
DllGetClassObject
DllRegisterServer
DllUnregisterServer
2.包含一个.idl命名的类型库文件,MIDL编译该idl文件生成的文件含义如下
AtlBaseCom_i.h 接口定义
AtlBaseCom_i.c 各种GUID定义
AtlBaseCom_p.h 代理实现
3.包含两个工程,工程名带PS后缀的为存根/代理代码。
2.新建组件对象
在工程名上左键选择添加类->ATL->简单ATL对象,填写组件对象名CoClass和接口名,其他的会自动生成。
下一步,如下,选择单线程自定义接口实现,不需要实现聚合
3.COM实现
此时我们切换到类视图上,在ICat接口上右键添加对应的方法,然后我们直接编辑idl来自动生成文件,打开idl发现当前idl如下:
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(7D796BB3-E479-42C9-99F9-FC2189CF8E78),
helpstring("ICat 接口"),
pointer_default(unique)
]
interface ICat : IUnknown{
[helpstring("方法SayHello")] HRESULT SayHello1([in] WCHAR* szWord);
};
[
uuid(63CD81C0-FD49-4153-A6CF-56BC8BA97935),
version(1.0),
helpstring("CAnimalObject Type Library")
]
library AtlBaseComLib
{
importlib("stdole2.tlb");
[
uuid(A0A0C1F6-B5F4-42D1-80A2-C4D47B99DC2D),
helpstring("AnimalObject 组件对象")
]
coclass CAnimalObject
{
[default] interface ICat;
};
};
可以看到,这里定义了ICat接口,AtlBaseComLib类型库,CAnimalObject组件对象。
此时编译工程,可以查看到生成的文件
AtlBaseCom_i.h 接口定义
AtlBaseCom_i.c 各种GUID定义
所有的事情都帮我们做好了,我们只需要实现对应接口即可。
我们手动添加上IDog接口,整个IDL如下:
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(7D796BB3-E479-42C9-99F9-FC2189CF8E78),
helpstring("ICat 接口"),
pointer_default(unique)
]
interface ICat : IUnknown{
[helpstring("方法SayHello")] HRESULT SayHello1([in] WCHAR* szWord);
};
[
uuid(4BF862D3-6677-46AB-AA0A-59012ACBAC10),
version(1.0),
helpstring("IDog 接口")
]
interface IDog : IUnknown{
[helpstring("方法SayHello")] HRESULT SayHello2([in] WCHAR* szWord);
};
[
uuid(63CD81C0-FD49-4153-A6CF-56BC8BA97935),
version(1.0),
helpstring("CAnimalObject Type Library")
]
library AtlBaseComLib
{
importlib("stdole2.tlb");
[
uuid(A0A0C1F6-B5F4-42D1-80A2-C4D47B99DC2D),
helpstring("AnimalObject 组件对象")
]
coclass CAnimalObject
{
[default] interface ICat;
interface IDog;
};
};
编译后,我们实现对应的接口如下即可,就是这么简单!
HRESULT STDMETHODCALLTYPE CCAnimalObject::SayHello1(WCHAR *szWord)
{
wcout << L"喵~ 猫大王发话: " << szWord << endl;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CCAnimalObject::SayHello2(WCHAR *szWord)
{
wcout << L"汪~ 狗大王发话: " << szWord << endl;
return S_OK;
}
在需要使用组件对象时,拷贝生成的.h和.c文件以获得对应的接口定义。
4.ATL实现原理简介
下面大概说下ATL的实现原理,查看生成的代码,组件对象定义如下:
class ATL_NO_VTABLE CCAnimalObject :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CCAnimalObject, &CLSID_CAnimalObject>,
public ICat,
public IDog
{
public:
CCAnimalObject()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_CANIMALOBJECT)
DECLARE_NOT_AGGREGATABLE(CCAnimalObject)
BEGIN_COM_MAP(CCAnimalObject)
COM_INTERFACE_ENTRY(ICat)
COM_INTERFACE_ENTRY(IDog)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
public:
STDMETHOD(SayHello1)( WCHAR* szWord);
STDMETHOD(SayHello2)( WCHAR* szWord);
};
OBJECT_ENTRY_AUTO(__uuidof(CAnimalObject), CCAnimalObject)
可以看到,这里继承了CComObjectRootEx和CComCoClass。
CComObjectRootEx继承自CComObjectRootBase,CComObjectRootBase实现基本的AddRef/Release/QueryInterface,CComObjectRootEx指定对应的线程模型。
CComCoClass包含具体的工厂类实现。
OBJECT_ENTRY_AUTO指定具体的工厂类对象。
DECLARE_REGISTRY_RESOURCEID指明了组件对象的注册信息,对应在.rgs文件中的描述。
DECLARE_NOT_AGGREGATABLE定义了CComCreator2,CComCreator2支持指定聚合和非聚合时的创建者CComCreator。工厂类调用CComCreator2创建组件对象。
CComCreator包装CComObject<CAnimalObject>,实现安全的创建。
DECLARE_PROTECT_FINAL_CONSTRUCT和FinalConstruct是为了支持多步构造,可在FinalConstruct中做自己的创建工作,并据此返回创建是否成功。FinalConstruct在CComCreator中调用。
CComObject完成了实际的AddRef/Release/QueryInterface调用。这里实现QueryInterface时查询BEGIN_COM_MAP和END_COM_MAP中指定的接口表。
组件对象析构时会调用FinalRelease.
详细的过程分析可参考《深入解析ATL》
本文完整演示代码下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219