分享
 
 
 

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

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

用ATL建立轻量级的COM对象

第三部分

作者:赵湘宁

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

第二部分:起步篇。

实现IUnknown

用纯粹的C++实现IUnknown相对来说比较简单。IUnknown实现之间的主要差别重点在于QueryInterface中将给出哪些接口。请看下列接口定义:

interface IMessageSource : IUnknown {

HRESULT GetNextMessage([out] OLECHAR **ppwsz);

}

interface IPager : IUnknown {

HRESULT SendMessage([in] const OLECHAR *pwsz);

}

interface IPager2 : IPager {

HRESULT SendUrgentMessage(void);

}

这些C++类定义实现了三个接口:

class CPager : public IMessageSource, public IPager2 {

LONG m_dwRef;

public:

CPager(void) :

m_dwRef(0) {}

virtual ~CPager(void) {}

STDMETHODIMP

QueryInterface(REFIID,

void**);

STDMETHODIMP_(ULONG)

AddRef(void);

STDMETHODIMP_(ULONG)

Release(void);

STDMETHODIMP GetNextMessage(OLECHAR **ppwsz);

STDMETHODIMP SendMessage(const COLECHAR * pwsz);

STDMETHODIMP SendUrgentMessage(void);

};

如果在堆中创建对象(也就是说用new操作符在内部创建)并且只用单线程公寓(STA)模式运行,下面是合理的AddRef 和Release实现:

STDMETHODIMP_(ULONG) CPager::AddRef() {

return ++m_dwRef;

}

STDMETHODIMP_(ULONG) CPager::Release(){

ULONG result = -m_dwRef;

if (result == 0)

delete this;

return result;

}

如果输出的对象是以多线程公寓(MTA)模式运行,则++和--操作符就必须用Win32的原子增量和减量(Increment/Decrement)例程调用来代替:

STDMETHODIMP_(ULONG) CPager::AddRef() {

return InterlockedIncrement(&m_dwRef);

}

STDMETHODIMP_(ULONG) CPager::Release(){

ULONG result = InterlockedDecrement(&m_dwRef);

if (result == 0)

delete this;

return result;

}

无论哪一种线程模式,下面的QueryInterface实现都是正确的:

STDMETHODIMP CPager::QueryInterface(REFIID riid, void **ppv) {

if (riid == IID_IUnknown)

*ppv = (IMessageSource*)this;

else if (riid == IID_IMessageSource)

*ppv = (IMessageSource*)this;

else if (riid == IID_IPager)

*ppv = (IPager*)this;

else if (riid == IID_IPager2)

*ppv = (IPager2*)this;

else

return (*ppv = 0), E_NOINTERFACE;

((IUnknown*)*ppv)->AddRef();

return S_OK;

}

QueryInterface的最后四行代码对所有的对象都一样。其余的部分则根据这个对象类型层次上的类不同而有所不同。

如果IUnknown的实现能形成规律,那么ATL便可以提供一种机制将这些具有共性的程序语句从代码中提取出来。实际上ATL做到了这一点,方法是通过提供灵活和可扩展的类层次,使得开发人员只要正确地说明所使用的类集,就可确定线程,服务器锁定和对象生命期行为。

如果看一看ATL实现的IUnknown类层次,你碰到的第一个参数化行为就是线程。ATL允许你构造被优化的对象和服务器,在相互转换STA和MTA的用法时,不用修改源代码。缺省情况下,ATL工程构造的是安全的MTA(MTA-safe)对象,除了要具备构造单STA对象所需的一切外,还需要附加代码和状态管理。通过定义适当的预处理指令,你能改变缺省的线程行为,从而支持单STA或多个基于STA的工程。

使用如下这个指令:

/D _ATL_SINGLE_THREADED

来编译工程可以改变服务器缺省的线程模型,让它只支持一个基于STA的线程。它适合于进程外的且不创建自拥有线程的基于STA的服务器情况,当你用这个选项时,所有对ATL全局状态的存取将都是不加锁的,并发的。尽管此选项似乎很有效,但它实质上限制了ATL服务器只能是一个单线程的。

使用如下这个指令:

/D _ATL_APARTMENT_THREADED

来编译工程可以改变服务器缺省的线程模型支持多个基于STA的线程。它适合于建立注册表项ThreadingModel=Apartment的进程内服务器。如果要创建基于STA的进程外服务器且还要建立附加的基于STA的线程,那么这个指令也是必须的。使用这个选项导致ATL用能安全存取线程的临界区来保护它的全局状态。

使用如下这个指令:

/D _ATL_FREE_THREADED

可以创建与任何线程环境兼容的服务器。也就是说ATL的全局状态将在临界区中被锁定,并且每个对象将拥有它自己的私有临界区来保护它的实例状态。如果没有定义这些指令,则ATL头文件假设为使用_ATL_FREE_THREADED。

为了在ATL中正确使用线程,必须理解ATL的线程模型概念。ATL定义了三种线程模型类来实现线程安全行为所需的少数内联操作。每一种线程模型都输出两个静态函数,Increment

和Decrement。它们都有一个长整指针参数,并且对指定的线程模型实现最快的,合法的增减操作。每一种线程模型都输出两个嵌套类型定义(typedefs),即AutoCriticalSection

和CriticalSection,它们要么打包Win32的CRITICAL_SECTION,要么就是出于兼容性考虑的空类,没有实际实现。记住,所用临界区实际使用的实际类型依赖于特定的线程模型。

ATL实现的三种线程模型分别是CComMultiThreadModel,CComSingleThreadModel和

CComMultiThreadModelNoCS。CComMultiThreadModel使用InterlockedIncrement/InterlockedDecrement和实的CRITICAL_SECTIONS。CComSingleThreadModel使用更有效的++和-操作符及虚的CRITICAL_SECTIONS。

混合的CComMultiThreadModelNoCS除了使用虚的CRITICAL_SECTIONS外,还有InterlockedIncrement/InterlockedDecrement。第三种模型对于存在于MTAs中,但不需要任何数据成员的锁定的对象很有用。

ATL提供了两个类型定义,CComObjectThreadModel 和 CComGlobalsThreadModel,通过条件编译来保证对象和全局变量各自的效率及行为安全。依据所定义的三种预编译指令之一,每一类型名对应着以上描述的三种线程模型类之一。下表说明了这种对应关系,它依赖于ATL所使用的预处理指令。

ATL类型定义

_ATL_SINGLE_THREADED

_ATL_APARTMENT_THREADED

_ATL_FREE_THREADED

CComGlobalsThreadModel

CComSingleThreadModel

CComMultiThreadModel

CComMultiThreadModel

CComObjectThreadModel

CComSingleThreadModel

CComSingleThreadModel

CComMultiThreadModel

只要给定了上述的线程模型类型层次,你就能将相应的参数化线程行为添加到任何COM类。请看下列代码:

参数化的线程

class CPager : public IPager {

LONG m_dwRef;

typedef CComObjectThreadModel _ThreadModel;

_ThreadModel::CComAutoCriticalSection m_critsec;

: : : :

STDMETHODIMP_(ULONG) CPager::AddRef() {

return _ThreadModel::Increment(&m_dwRef);

}

STDMETHODIMP_(ULONG) CPager::Release(){

ULONG res = _ThreadModel::Decrement(&m_dwRef);

if (res == 0)

delete this;

return res;

}

STDMEHTHODIMP SendUrgentMessage() {

// 保证只有一个线程

m_critsec.Lock();

// 实现任务

this->GenerateMessage();

this->WakeUpUser();

// 允许其它线程

m_critsec.Unlock();

return S_OK;

}

};

使用缺省选项(_ATL_FREE_THREADED)的编译则将一个实临界区添加到对象,并执行Lock和Unlock方法将内联调用映射到EnterCriticalSection/LeaveCriticalSection

API函数。同时,AddRef和Release方法将使用InterlockedIncrement/InterlockedDecrement来安全地改变这个对象的引用计数。

如果前面的代码是用_ATL_APARTMENT_THREADED 或者 _ATL_SINGLE_ THREADED选项编译的,则m_critsee数据成员将为空,Lock和Unlock内联例程将变成虚操作,并且AddRef和Release方法将使用++和--操作符。如果这个对象是一个ActiveX控件且其线程模型为Apartment

(ThreadingModel=Apartment)的进程内服务器,则这将是小而快的代码。

有时构造以MTA模式(即它的AddRef和Release必须是线程安全的)运行,但不需要附加锁定的对象很有用。对于这种类型的对象,混合型的CComMultiThreadModelNoCS很适合。

通过将类的类型定义从:

typedef CComObjectThreadModel _ThreadModel;

细化到

typedef CComMultiThreadModelNoCS _ThreadModel;

那么针对每一个对象,你不必付出CRITICAL_SECTION的开销(CComAutoCriticalSection

会映射到

CComFakeCriticalSection)就可以得到线程安全的AddRef和Release(将Increment 和

Decrement方法映射到InterlockedIncrement和InterlockedDecrement)。(待续)

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