分享
 
 
 

ATL中IUnknown接口的实现(一)

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

ATL中IUnknown接口的实现(一)

ATL和MFC都能作为开发COM的工具,而ATL是一个Template Library,MFC却是一个FrameWork,作为Template Library,其优势在于其只是提供给程序员形式各异,可以用来构建软件的小积木,由程序员的意愿架构出任意形状的软件大厦;而FrameWork却是已经把大厦的主体建立好,我们只需要往里面添砖加瓦,做一些细节上的修整,以让其符合我们的要求。两者各有千秋。不过由于Template拥有区别于OO的弹性(通过指定特定的模板参数产生符合要求的模板类和模板函数)以及Template的具现化是在编译期间实现,运行效率高,所以与FrameWork相比,Template Library给人的感觉更加灵活、精致、有效。

COM技术的核心是IUnknown接口,说具体点,就是AddRef()、Release()、QueryInterface()三个方法的具体实现。前两个接口的复杂性在于COM拥有不同的线程模型:微软定义的COM线程模型有3种:STA、MTA以及Free,三种模型下,引用计数的处理均有不同;最后一个接口的作用是接口查询——给我一个riid,我能给出这个riid的指针供你调用。它的复杂性主要体现在COM对象被聚合的时候如何能得到正确的接口指针。虽然情况有很多种,可是一旦确定了线程模型和COM对象的实体身份,我们就能实作出这三个接口的通用的动作方法。ATL正是帮我们完成了这个工作,我们只需要将COM对象的线程模型、实体身份作为模板参数传递给ATL,ATL就会自动为我们产生出正确的代码——而这一切均是在编译期间确定的。

对于引用计数的处理,无非是两个动作:引用计数的加一和减一。多线程时加减的操作由API函数InterlockedIncrement()和InterlockedDecrement()完成;单线程时,简单地++m_cRef和—m_Ref即可。作为一个Library,对这些操作进行一定的包装是非常必要的,我们需要通过这个包装,给外界提供一致的接口。同时,由于对象被聚合时,引用计数的操作又不一样,所以,我们所需要的包装不只一层,不只需要InternalAddRef(),还应该需要OutAddRef()。下面先来看InternalAddRef()的实现。

ATL中把不同线程模型下引用计数的实作封装到同一个函数中:加法是Increment(),减法是Decrement()。三个代表不同线程模型的类:CComSingleThreadModel、CComMultiThreadModel 、CComMultiThreadModelNoCS中均实作了这两个函数。这样,我们只需要把线程模型当作模板参数传递给对象,对象就能获得正确的引用计数操作动作。

同时,既然已经有了线程模型的定义,那么“临界区”也就顺理成章地应该在这些线程模型的类中得到定义——这样也给“临界区”的操作提供了统一的界面。我们只需要在任何操作变量的时候,都使用这个界面操作,那么,只需要更改一个线程模型参数,我们的程序就可

以由单线程支持变成多线程支持。这三个类的实作如下:

//From ATLbase.h

class CComMultiThreadModelNoCS

{

public:

static ULONG WINAPI Increment(LPLONG p) throw()

{return InterlockedIncrement(p);}

static ULONG WINAPI Decrement(LPLONG p) throw()

{return InterlockedDecrement(p);}

typedef CComFakeCriticalSection AutoCriticalSection;

typedef CComFakeCriticalSection CriticalSection;

typedef CComMultiThreadModelNoCS ThreadModelNoCS;

};

class CComMultiThreadModel

{

public:

static ULONG WINAPI Increment(LPLONG p) throw()

{return InterlockedIncrement(p);}

static ULONG WINAPI Decrement(LPLONG p) throw()

{return InterlockedDecrement(p);}

typedef CComAutoCriticalSection AutoCriticalSection;

typedef CComCriticalSection CriticalSection;

typedef CComMultiThreadModelNoCS ThreadModelNoCS;

};

class CComSingleThreadModel

{

public:

static ULONG WINAPI Increment(LPLONG p) throw() {return ++(*p);}

static ULONG WINAPI Decrement(LPLONG p) throw() {return --(*p);}

typedef CComFakeCriticalSection AutoCriticalSection;

typedef CComFakeCriticalSection CriticalSection;

typedef CComSingleThreadModel ThreadModelNoCS;

};

ATL中有3种临界区CComCriticalSection、CComAutoCriticalSection、CComFakeCriticalSection。每一个临界区都提供了统一的4种操作来操作临界区:Lock()、Unlock()、Init()、Term() 。我们在程序中使用“临界区”时,只需要调用这四种操作就可以达到保护数据成员不被其他线程修改的目的。调用方法如下:

ThreadingModel::AutoCriticalSection m_cs ;

m_cs.Lock();

m_cs.Unlock();

三种临界区的实作如下:

//From ATLCore.h

class CComCriticalSection

{

public:

CComCriticalSection() throw()

{

memset(&m_sec, 0, sizeof(CRITICAL_SECTION));

}

HRESULT Lock() throw()

{

EnterCriticalSection(&m_sec);

return S_OK;

}

HRESULT Unlock() throw()

{

LeaveCriticalSection(&m_sec);

return S_OK;

}

HRESULT Init() throw()

{

HRESULT hRes = S_OK;

__try

{

InitializeCriticalSection(&m_sec);

}

// structured exception may be raised in low memory situations

__except(EXCEPTION_EXECUTE_HANDLER)

{

if (STATUS_NO_MEMORY == GetExceptionCode())

hRes = E_OUTOFMEMORY;

else

hRes = E_FAIL;

}

return hRes;

}

HRESULT Term() throw()

{

DeleteCriticalSection(&m_sec);

return S_OK;

}

CRITICAL_SECTION m_sec;

};

class CComAutoCriticalSection : public CComCriticalSection

{

public:

CComAutoCriticalSection()

{

HRESULT hr = CComCriticalSection::Init();

if (FAILED(hr))

AtlThrow(hr);

}

~CComAutoCriticalSection() throw()

{

CComCriticalSection::Term();

}

private :

HRESULT Init(); // Not implemented. CComAutoCriticalSection::Init should never be called

HRESULT Term(); // Not implemented. CComAutoCriticalSection::Term should never be called

};

class CComFakeCriticalSection

{

public:

HRESULT Lock() throw() { return S_OK; }

HRESULT Unlock() throw() { return S_OK; }

HRESULT Init() throw() { return S_OK; }

HRESULT Term() throw() { return S_OK; }

};

ATL提供了CComObjectRootEx模板类,来封装上面关于引用计数的操作以及临界区的操作。只要提供给CComObjectRootEx一个代表其线程模型的模板参数,它就能实作出InternalAddRef()、InternalRelease()以及Lock()和UnLock()四个统一的操作界面。

经过了这么多层封装,ATL已经把AddRef()和Release()所需要的操作全部实作出来了,可是ATL还是没有迈出最后的一步——没有把这些操作整合进AddRef()和Release(),这是因为ATL还要考虑聚合的因素。在聚合的情况下,COM对象的AddRef()和Release()操作都和独立激活时候完全不同。这是后话,下面来看看独立激活时QueryInterface()的实现。

有先贤曾云:表驱动、Hash表、Cache技术是计算机技术3个最重要的核心。微软对于表驱动技术的发挥在其类库是无所不用其极,MFC就是一个最典型的例子,就连孟岩这样的C++老手,也丝毫不掩饰其对MFC中“稀奇古怪”的宏的头疼。ATL中,微软又一次向我们展示了他令人眩目的表驱动实作手法:接口有接口映射表、对象有对象映射表、类别有类别映射表、连接点有连接点映射表、消息有消息映射表。每一个表带来的是什么?是宏,是静态的函数绑定,是执行期效率的显著提升,是跨越不同编译器ABI(应用程序二进制界面)的能力;当然,同时也是对程序代码的恶性膨胀,对C++多态特性的视而不见,对代理、委托等设计模式的退避三舍。(由此观之,是否觉得宏和模板有些类似?)

尽管表驱动有方方面面的优点和缺点,但是不可否认,QueryInterface()的运作机理很适合用表驱动来实现,从而达到美化代码,提高工作效率的目的。我们只需要把我们想要暴露的接口用如下的形式提供给ATL,ATL就会为我们实作出准确的QueryInterface():

BEGIN_COM_MAP(CMyClass)

COM_INTERFACE_ENTRY(IMyInterface)

END_COM_MAP

想暴露多少个接口,就写多少个COM_INTERFACE_ENTRY,ATL会自动用这些声明生成一个名为_ATL_INTMAP_ENTRY的接口表,然后在CComObjectRootBase类中提供一个InternalQueryInterface()的函数用这张表为参数,实作出正确的QueryInterface().当然,COM对象提供接口的方法有很多,COM_INTERFACE_ENTRY提供的只是最基本的输出接口的方法,其它的还有双接口、Tear-Off接口、聚合接口(又分为“有计划的”和“盲”的)、通过CHAIN得到基类的接口等,这些内容等以后再逐一写出。

除开上面这张表,BEGIN_COM_MAP另一个重要的作用是提供了一个GetUnknown()函数,这让我们在聚合、继承的时候,我们不会丢掉我们的IUnknown指针!

前面的讨论都是COM对象为普通对象,独立激活的时候的情况,当发生聚合等关系的时候,我们的类的实作会有另一番风景。期待下文吧!

由于本人学识所限,文章内容错在所难免,如有批评指正之词,请mail :firingme@sina.com

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有