分享
 
 
 

用ATL建立轻量级的COM对象(六)

王朝vc·作者佚名  2006-01-17
窄屏简体版  字體: |||超大  

用ATL建立轻量级的COM对象

第六部分

作者:赵湘宁

第一部分:为什么要使用ATL。

第二部分:起步篇。

第三部分:实现IUnknown。

第四部分:实现接口。

第五部分:不要过分抽象。

输出你的类

实现了 CComObject ,你就有足够的条件用 C++ new 操作符创建

COM

对象。不过这样做没有什么实用价值,因为毕竟外部客户端使用

CoCreateInstance 或 CoGetClassObject 创建类实例。也就是说,你必须为每个外部类输出类对象。幸运的是ATL分别在它的

CComClassFactory 和 CComClassFactory2 类中提供了缺省的 IClassFactory 和

IClassFactory2接口实现。

CComClassFactory

不是模板驱动类,但其中有一个函数指针作为数据成员,使用这个函数可以创建对象。ATL提供了一个类模板家族,它们都有一个单独的静态方法

CreateInstance,由 Creators 调用,Creators 提供正确的语义来从

CComClassFactory 创建基于 CComObjectRoot

的对象。下面的这段代码展示了缺省的创建机制:CComCreator,它产生一个模板化的类实例,并用

ATL 中标准的 FinalConstruct 来顺序初始化对象。

ATL Creator

template class CComCreator {

public:

static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv) {

HRESULT hRes = E_OUTOFMEMORY;

T1* p = NULL;

ATLTRY(p = new T1(pv))

if (p != NULL) {

p-SetVoid(pv);

p-InternalFinalConstructAddRef();

hRes = p-FinalConstruct();

p-InternalFinalConstructRelease();

if (hRes == S_OK)

hRes = p-QueryInterface(riid, ppv);

if (hRes != S_OK)

delete p;

}

return hRes;

}

};

template class CComFailCreator {

public:

static HRESULT WINAPI CreateInstance(void*, REFIID,

LPVOID*)

{ return hr; }

};

template class CComCreator2 {

public:

static HRESULT WINAPI CreateInstance(void* pv, REFIID riid,

LPVOID* ppv) {

HRESULT hRes = E_OUTOFMEMORY;

if (pv == NULL)

hRes = T1::CreateInstance(NULL, riid, ppv);

else

hRes = T2::CreateInstance(pv, riid, ppv);

return hRes;

}

};

因为 ATL 利用 Visual C++ 中的__declspec(novtable)

优化,所以在很大程度上依赖两层构造。declspec

取消掉了在抽象基类的构造函数中必须对 vptr 进行的初始化,因为抽象基类中的任何的

vptr 会在派生类中被重写。之所以要进行这种优化,是因为初始化从未被使用过的

vptr 毫无意义。另外,因为不需要为抽象基类分配vtable,从而减少了代码的大小。

使用这种技术的类(包括大多数 ATL

基类)需要当心,不要调用构造器中的虚函数。但是,为了在初始化时允许对虚函数的调用,ATL

的 Creators 调用 FinalConstruct 方法,在这个方法中进行所有重要的初始化工作。在

FinalConstuct 中,从C++的角度看,你的类已经完全构造好了,也就是说你的所有对象的

vptr 完全被派生化。同时,基于 CComObject

的打包器也同时构造好了,允许你存取在 COM 聚合或 tear-off

情况下无法知道的控制。

如果在调试器中单步顺序执行 Creator

调用,你将注意到在缺省情况下对 InternalFinalConstructAddRef 和 InternalFinalConstructRelease

的调用什么也没做,但是,如果你打算在你的 FinalConstruct

实现中创建 COM

聚合,你可能会临时增加一次对象的引用计数,以防止它过早销毁(这发生在某个聚合对象调用

QueryInterface时)。你能通过添加下面的类定义行进行自我保护:

DECLARE_PROTECT_FINAL_CONSTRUCT()

这一行代码重新定义了类的 InternalFinalConstructAddRef 和 InternalFinalConstructRelease

来增减引用计数,从而安全地传递可能调用 QueryInterface 的对象指针。

每一个基于ATL的工程都包含着一个 CComModule

派生类的实例。除了实现前面提到过的服务器生命期行为外,CComModule

还维持着一个 CLSID 到 ClassObject 的映射(叫做对象映射 Object Map)向量来提供所有外部可创建类。这个对象映射被用于实现进程内服务器的

DllGetClassObject,并且它为进程外服务器每次调用 CoRegisterClassObject

提供参数。虽然能直接显式地使用 CComClassFactory 和 Creator

类,但通常都是在 ATL 对象映射基础的上下文中使用。

ATL Object Map 是一个_ATL_OBJMAP_ ENTRY结构数组:

struct _ATL_OBJMAP_ENTRY {

const CLSID* pclsid;

HRESULT (*pfnUpdateRegistry)(BOOL bRegister);

HRESULT (*pfnGetClassObject)(void* pv,

REFIID riid, LPVOID* ppv);

HRESULT (*pfnCreateInstance)(void* pv,

REFIID riid, LPVOID* ppv);

IUnknown* pCF;

DWORD dwRegister;

LPCTSTR (* pfnGetObjectDescription)(void);

};

pfnGetClassObject成员的调用是在第一次需要创建新的类对象时。这个函数被作为

Creator 函数(pfnCreateInstance)的第一个参数传递,并且返回的结果接口指针被缓存在pCF成员中。通过按需要创建类对象,而不是静态地实例化变量,就不再需要使用带虚函数的全局对象,使得基于

ATL 的工程不用C运行库就能进行链接。(在 DllMain / WinMain

以前,C运行时必须用来构造全局和静态变量。)

虽然你可以显式地定义用于对象映射的各种函数,通常的方法是将

CComCoClass 添加到你自己类的基类列表中。CComCoClass

是一个模板类,它有两个模板参数:你自己的类名和对应的

CLSID 指针。它添加适当的类型定义和静态成员函数来提供对象映射必须的功能。下面的代码示范了

CComCoClass 的使用:

class CPager

: public CComObjectRootEx,

public CComCoClass,

public IPager

{

public:

BEGIN_COM_MAP(CPager)

COM_INTERFACE_ENTRY(IPager)

END_INTERFACE_MAP()

STDMETHODIMP SendMessage(const OLECHAR * pwsz);

};

一旦你从CComCoClass派生,你的类就已经被添加到ATL Object Map中。ATL所提供的用来简化建立对象映射的宏很像接口映射宏。下面就是为多CLSID服务器建立的一个对象映射。

BEGIN_OBJECT_MAP(ObjectMap)

OBJECT_ENTRY(CLSID_Pager, CPager)

OBJECT_ENTRY(CLSID_Laptop, CLaptop)

END_OBJECT_MAP()

这个代码建立了一个叫 ObjectMap 的 _ATL_OBJMAP_ENTRY 数组,初始化如下:

static _ATL_OBJMAP_ENTRY ObjectMap[] = {

{ &CLSID_Pager, &CPager::UpdateRegistry,

&CPager::_ClassFactoryCreatorClass::CreateInstance,

&CPager::_CreatorClass::CreateInstance, NULL,

0,

&CPager::GetObjectDescription

},

{ &CLSID_Laptop, &CLaptop::UpdateRegistry,

&CLaptop::_ClassFactoryCreatorClass::CreateInstance,

&CLaptop::_CreatorClass::CreateInstance, NULL,

0,

&CLaptop::GetObjectDescription

},

{ 0, 0, 0, 0 } };

静态成员函数从 CComCoClass 派生,被隐含式定义。以上定义的对象映射一般通过使用

CComModule 的 Init 方法被传递到ATL:

_Module.Init(ObjectMap, hInstance);

这个方法根据创建的服务器类型,在 DllMain 或 WinMain 中被调用。

缺省情况下,CcomCoClass

为你的类提供了一个标准的类工厂,允许客户端聚合你的对象。你可以通过添加下面的类定义代码行来改变缺省的聚合行为:

DECLARE_NOT_AGGREGATABLE(CPager)

DECLARE_ONLY_AGGREGATABLE(CPager)

DECLARE_POLY_AGGREGATABLE(CPager)

这些宏只是将 ATL Creator

定义成一个将被用于初始化对象映射的嵌套类型(CreatorClass)。前面两个宏是自解释的(它们禁止或需要聚合)。

第三个宏需要解释一下。缺省情况下,CComCoClass 使用 ATL

类创建机制,根据是否需要使用聚合来创建两个不同的类之一。如果不需要聚合,则创建新的

CComObject 实例。如果需要聚合,则创建新的CComAggObject实例。也就是说两个不同的

vtables 必须在可执行文件中出现。对照之下,DECLARE_POLY_ AGGREGATABLE

总是创建一个 CComPolyObject

实例,并根据对象是否聚合来初始化这个外部控制指针。亦即只要定义一个C++类,只需一个

vtable。这个技术的不足之处是:非聚合对象的每个实例必须为非代理

IUnknown 指针多用4个字节。不论哪种情况,支持聚合都不需要实际的编码,而只是在实例和代码大小之间作出取舍。(待续)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有