ATL接口映射宏详解

上传人:z**** 文档编号:114224813 上传时间:2022-06-28 格式:DOC 页数:11 大小:59.50KB
收藏 版权申诉 举报 下载
ATL接口映射宏详解_第1页
第1页 / 共11页
ATL接口映射宏详解_第2页
第2页 / 共11页
ATL接口映射宏详解_第3页
第3页 / 共11页
资源描述:

《ATL接口映射宏详解》由会员分享,可在线阅读,更多相关《ATL接口映射宏详解(11页珍藏版)》请在装配图网上搜索。

1、ATL 接口映射宏详解(下)五. COM_INTERFACE_ENTRY_AGGREGATE(iid, punk)参 ATL 例程 COMMAP这一节中将介绍ATL中用于聚集对象的宏。聚集对象的概念请参阅其它参考书。 现在先看一看这个宏的典型用法:class CAgg :public IDispatchImpl,public ISupportErrorInfo,public CComObjectRoot,public CComCoClass ;CAgg是一个聚集类,它的实现与一般的ATL组件没有区别,只是注意在它的类定义中不要加 入 DECLARE_NO_AGGREGATABLE.class

2、COuter :public CChainBase,public IDispatchImpl,public CComCoClassHRESULT FinalConstruct();void FinalRelease();BEGIN_COM_MAP(COuter)COM_INTERFACE_ENTRY_AGGREGATE(IID_IAgg, m_pUnkAgg.p)END_COM_MAP()DECLARE_GET_CONTROLLING_UNKNOWN()CComPtr m_pUnkAgg;COuter 包含了聚合组件 CAgg, 它包含了几个不同之处:(1) 加入了 COM_INTERFACE

3、_ENTRY_AGGREGATE(IID_IAgg, m_pUnkAgg.p)宏。#define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk)&iid,(DWORD)offsetof(_ComMapClass, punk),_Delegate,offsetof 我们在上一节中已经见过,可以猜到它求的就是 punk 在类中的位置。也就是 m_pUnkAgg 在 COuter 中的位置。(2) 加入了宏 DECLARE_GET_CONTROLLING_UNKNOWN(),其定义为:#define DECLARE_GET_CONTROLLING_UNKNOWN()

4、 public:virtual IUnknown* GetControllingUnknown() return GetUnknown();我们也没必要继续深究下去,仅从字面意思就可以看出这个函数将返回组件的 IUnknown 指 针。(3) 在 COuter 中加入一个成员变量:CComPtr IUnknown m_pUnkAgg; m_pUnkAgg将用于获 得被聚集组件的 IUnknown 指针。(4) 重载了 FinalConstruct,FinalReleaseHRESULT COuter:FinalConstruct()IUnknown* pUnkOuter = GetContro

5、llingUnknown();HRESULT hRes = CoCreateInstance(CLSID_CAgg, pUnkOuter, CLSCTX_ALL, IID_IUnknown, (void*)&m_pUnkAgg);return hRes;void COuter:FinalRelease()m_pUnkAgg.Release();当创建组件COuter后将会调用FinalConstruet,所以会在这里创建聚集组件。原则上聚集 组件可以仅在需要的时候才创建,但也可以随着包含它的组件一起创建。聚集组件的创建没 什么特别之处,只是要注意它将查询IUnknown指针,并返回给m_pUn

6、kAgg.外部组件将通过 m_pUnkAgg 操作聚集组件。另外注意到使用 pUnkOuter 作为 CoCreateInstanee 的参数,这将 导致创建 CComAggObjeet COuter 对象,内部包含一个 CComContainedObjeet 的包含对象。 与上一节中的 CComCachedTearOff类似,CComAggObjeet COuter也不是从 COuter 派生 的, 所以真正的组件对象不是 CComAggObjeet COuter 对象, 而是它内部包含的 CComContainedObject COuter 对象。同样 pUnkOuter 得到的将是 CC

7、omAggObjeet的 IUnknown 指针,也同样调用它的 QueryInterface 会转而调用 CComContainedObject 的 _InternalQueryInterface 函数(呵呵,现在可都还是我猜的,看我猜的对不对吧) 运行 pOuter-QueryInterface(IID_IAgg, (void *)&pAggl)函数堆栈一:9.ATL:AtlInternalQueryInterface(.)8. ATL:CComObjectRootBase:InternalQueryInterface(.)7.CAgg:_InternalQueryInterface(.)

8、6.ATL:CComAggObject:QueryInterface(.) 5.ATL:CComObjectRootBase:_Delegate(.) 4.ATL:AtlInternalQueryInterface(.)3.ATL:CComObjectRootBase:InternalQueryInterface(.) 2.COuter:_InternalQueryInterface(.)1.ATL:CComObject:QueryInterface(.) 解释:1-5:这几步函数调用我们已经见了很多次了,因为在这个宏定义使用了_Delegate,所以将调 用 CComObjectRootBa

9、se:_Delegate(.).static HRESULT _Delegate(void* pv,REFIID iid,void* ppvObject,DWORD dw)HRESULT hRes = E_NOINTERFACE;IUnknown* p = *(IUnknown*)(DWORD)pv + dw);if (p != NULL) hRes = p-QueryInterface(iid, ppvObject); return hRes;第二句话的含义我们在上一节中已经见过了,最后的结果p=COu ter:m_pUnkAgg.6:正如我们刚才所料,现在调用的是CComAggObjec

10、t CAgg:Queryin terface()STDMETHOD(QueryInterface)(REFIID iid, void * ppvObject)/如果查询的是 iUnknown, 则elsehRes = m_contained._internalQueryinterface(iid, ppvObject); return hRes;也正如我们所料,将交给它的包含对象去做.(这段代码在上一节好象也见过是吧,呵呵)7-9:同上一节一样,将交给 CAgg:_InternalQueryInterface(.),剩下的工作将由 CAgg 完 成了。最后返回的指针实际上将是CComCon t

11、ainedObjec t CAgg组件的接口指针。运行 pAgg1-QueryInterface(IID_IAgg, (void *)&pAgg2)函数堆栈二:9. CAgg:_InternalQueryInterface(.)8.ATL:CComAggObject CAgg :QueryInterface(.) 7.ATL:CComObjectRootBase:_Delegate(.) 6.ATL:AtlInternalQueryInterface(.)5.ATL:CComObjectRootBase:InternalQueryInterface(.) 4.COuter:_InternalQ

12、ueryInterface(.)3.ATL:CComObject:QueryInterface(.)2.ATL:CComObjectRootBase:OuterQueryInterface(.) 1.ATL:CComContainedObject:QueryInterface(.)解释:1-9:浏览整个堆栈,与我们上一节所见的堆栈二太相近了,这是因为都是使用了包含对象。 包含对象起了个代理的作用,他先把查询交给外部对象(COuter)去做(第1,2步),当外部对 象发现要查询的是聚集组件的接口时(IAgg),就会再把查询交还给它保留的聚集组件的指针 (m_pUnkAgg,第7步中,注意这不是真

13、正的聚集组件),m_pUnkAgg再把查询交给包含对象(第8 步中),包含对象再把查询交给真正实现接口的类CAgg(第9步).若外部对象发现要查询的是 外部组件的接口时,那就很简单了,直接查询就行了。这样就防止了外部组件与聚集组件查 询操作的不一致性。唉,真个过程真麻烦,不过还好,与上一节的宏很类似。相关的源码可 参看上一节。六、COM_INTERFACE_ENTRY_AGGREGATE_BLIND 参 ATL 例程 COMMAP上一节我们讲了 COM_INTERFACE_ENTRY_AGGREGATE,这节要介绍的宏与它很类似。#define COM_INTERFACE_ENTRY_AGGR

14、EGATE_BLIND(punk)NULL,(DWORD)offsetof(_ComMapClass, punk),_Delegate,从定义上就可以看出,它与上一节介绍宏的唯一区别就在于,它没有指明接口 ID! 所以在它的定义中第一项也是NULL。这个宏的用法与我们COM_INTERFACE_ENTRY_AGGREGATE 模一样。大家可以参考上一节内容 以及ATL的例程COMMAP0我们来看看At llnternalQuerylnterface()中的相关代码。ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis,const _ATL

15、_INTMAP_ENTRY* pEntries, REFIID iid, void* ppvObject)/如果是 IUnknown,while (pEntries-pFunc != NULL)BOOL bBlind = (pEntries-piid = NULL);if (bBlind | InlineIsEqualGUID(*(pEntries-piid), iid)if (pEntries-pFunc = _ATL_SIMPLEMAPENTRY) /offsetATLASSERT(!bBlind);IUnknown* pUnk = (IUnknown*)(int)pThis+pEntri

16、es-dw);pUnk-AddRef();*ppvObject = pUnk;return S_OK;elseHRESULT hRes = pEntries-pFunc(pThis, iid, ppvObject, pEntries-dw);if (hRes = S_OK | (!bBlind & FAILED(hRes) return hRes;pEntries+;return E_NOINTERFACE;注意变量 bBlind:BOOL bBlind = (pEntries-piid = NULL);若没指定接口 ID,也继续执行后面的操作,可见即使并非我们所需要的IID,也会执行 _De

17、legate.从上可见,这个宏适用于一个聚集组件有多个接口的情况,这样只要是查询这个聚集组件的接口,就会进入_Delegate函数。但要特别注意的是这个宏的位置!!比如若是这样的顺序:BEGIN_COM_MAPCOM_INTERFACE_ENTRY_AGGREGATE_BLIND(m_pUnkAggBlind.p) COM_INTERFACE_ENTRY(IOuter)END_COM_MAP当查询IOuter接口时就会出错! !七、COM_INTERFACE_ENTRY_AUTOAGGREGATE(iid, punk, clsid)参 ATL 例程 COMMAP先看看这个宏的定义:#defin

18、e COM_INTERFACE_ENTRY_AUTOAGGREGATE(iid, punk, clsid)&iid,(DWORD)&_CComCacheDataCComAggregateCreator,(DWORD)offsetof(_ComMapClass, punk):data,_Cache,先看看它的典型用法:class CAutoAgg :public IDispatchImpl, public ISupportErrorInfo,public CComObjectRoot,public CComCoClass ;与一般的组件并无二样。class COuter :public CCha

19、inBase,public IDispatchImpl,public CComCoClassBEGIN_COM_MAP(COuter)COM_INTERFACE_ENTRY_AUTOAGGREGATE(IID_IAutoAgg, m_pUnkAutoAgg.p, CLSID_CAutoAgg) END_COM_MAP()CComPtr m_pUnkAutoAgg;与宏 COM_INTERFACE_ENTRY_AGGREGRATE(_)不同,Outer 不用在 FinalConstruct 中创建聚集 组件。外部组件会自动创建聚集组件!1。template _ATL_CACHEDATA _CCo

20、mCacheData:data = dwVar, Creator:Creat eInstance;2。static HRESULT WINAPI _Cache(void* pv, REFIID iid, void* ppvObject, DWORD dw)HRESULT hRes = E_NOINTERFACE;_ATL_CACHEDATA* pcd = (_ATL_CACHEDATA*)dw;IUnknown* pp = (IUnknown*)(DWORD)pv + pcd-dwOffsetVar);if (*pp = NULL) hRes = pcd-pFunc(pv, IID_IUnkn

21、own, (void*)pp);if (*pp != NULL) hRes = (*pp)-QueryInterface(iid, ppvObject);return hRes;3。template class CComAggregateCreatorpublic:static HRESULT WINAPI CreateInstance(void* pv, REFIID/*riid*/, LPVOID* ppv ) ATLASSERT(*ppv = NULL);ATLASSERT(pv != NULL);T* p = (T*) pv;return CoCreateInstance(*pclsi

22、d, p-GetControllingUnknown(), CLSCTX_ALL, IID_IUnknown, ppv);因为 _Cache,_CComCacheData,CComAggregateCreator 这几个类和函数我们已经在前面见过或者见过类似的,所以就不再多讲了。总之我们可以看到,若m_pUnkAutoAgg.p不为空则直 接查询,否则创建聚集组件。与宏 COM_INTERFACE_ENTRY_AGGREGATE 相比,这个宏似乎更好一些,仅当需要时才会创建, 使用更简单。八、COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND( punk, clsi

23、d )参 ATL 例程 COMMAP看看它的定义:#define COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(punk, clsid)NULL,(DWORD)&_CComCacheDataCComAggregateCreator,(DWORD)offsetof(_ComMapClass, punk):data,_Cache,呵 呵 , 这 个 宏 综 合 了 COM_INTERFACE_ENTRY_AUTOAGGREGATE() 和 COM_INTERFACE_ENTRY_AGGREGATE_BLIND() 的特点,既可以自动创建也可以很方便地查询聚 集组件

24、中的多个接口。不再赘述!九、COM_INTERFACE_ENTRY_CHAIN(classname)参 ATL 例程 COMMAP先看看它的定义:#define COM_INTERFACE_ENTRY_CHAIN(classname)NULL,(DWORD)&_CComChainData:data, _Chain,典型用法:class CChain :public IDispatchImpl,public ISupportErrorInfo,public CComObjectRoot,public CComCoClass ;它与一般的组件无异。class COuter : public CCh

25、ain,BEGIN_COM_MAP(COuter)COM_INTERFACE_ENTRY_CHAIN(CChain)END_COM_MAP();我们对查询的过程已经很熟悉了,可以直接来看看_Chain的功能。_Chain()是 CComObjectRootBase 的成员函数:static HRESULT WINAPI _Chain(void* pv, REFIID iid, void* ppvObject,DWORD dw) _ATL_CHAINDATA* pcd = (_ATL_CHAINDATA*)dw;void* p = (void*)(DWORD)pv + pcd-dwOffset)

26、;return InternalQueryInterface(p, pcd-pFunc(), iid, ppvObject);struct _ATL_CHAINDATADWORD dwOffset;const _ATL_INTMAP_ENTRY* (WINAPI *pFunc)();我们再看看宏定义中的 dw 部分:template _ATL_CHAINDATA _CComChainData:data = offsetofclass(base, derived), base:_GetEntries;基本上我们已经看懂是怎么回事了,void *p将得到基类的指针,InteralQuerylnte

27、rface我 们已经很熟悉了,Chain把基类的指针以及基类的接口映射宏传给它,实际上是查询基类的 接口!一般情况下把这个宏放在BEGIN_COM_MAP和END_COM_MAP之间的最后面,这表示只有在当前 类中查不到接口时才去查父类的接口。不过也经常把它放在第一位,这时就是先去查父类接 口,只有父类没有实现这种接口时才查自己。在 ATL 中组件是以多重继承的方式实现的, ATL 定义了很多类实现了一些常用的接口,这些类经常被做为组件的基类,所以这个宏被大量使 用。所有重要的宏我们都已经讲过了,剩下的都是些很简单的宏了.呵呵,还是把它们都罗列一下, 善始善终嘛.十、COM_INTERFACE

28、_ENTRY_IID(iid, x)#define COM_INTERFACE_ENTRY_IID(iid, x)&iid,offsetofclass(x, _ComMapClass),_ATL_SIMPLEMAPENTRY,十一、 COM_INTERFACE_ENTRY2_IID(iid, x, x2)#define COM_INTERFACE_ENTRY2_IID(iid, x, x2)&iid,(DWORD)(x*)(x2*)(_ComMapClass*)8)-8,_ATL_SIMPLEMAPENTRY,从定义上看这两个宏与COM_INTERFACE_ENTRY()和C0M_INTERF

29、ACE_ENTRY2()相比,都只是多 了一项iid。没有别的好处,只不过由用户明确指出接口IID,而不用系统根据接口名字去 转换了。十二、 COM_INTERFACE_ENTRY_FUNC( iid, dw, func )#define COM_INTERFACE_ENTRY_FUNC(iid, dw, func)&iid, dw, func,还记得 AtlInternalQueryInterface() 中的代码吗?如果在接口映射表中找到了我们要找的 接口,并且这个接口不是_ATL_SIMPLEENTRY型的,则执行宏定义中的指定的函数。 这个宏就给我们提供了自己编写处理函数的功能。这个函

30、数必须是如下定义:HRESULT WINAPI func(void* pv, REFIID riid, LPVOID* ppv, DWORD dw);当AtllnternalQuerylnterface调用func时,会传进相关的信息。pv是类对象的指针,riid 是要查询的接口,ppv是要返回查询得到的接口指针,dw是在宏定义中指定的参数。另外如 果函数中不打算返回接口指针,则应把ppv赋为NULL,并返回S_FALSE或E_NOINTERFACE。 返回S_FALSE刚会继续查找下去,若返回E_NOINTERFACE则会终止查询。若返回接口指针, 则应返回 S_OK.十三、 COM_INT

31、ERFACE_ENTRY_FUNC_BLIND(dw, func)#define COM_INTERFACE_ENTRY_FUNC_BLIND(dw, func)NULL, dw, func,至于_BLIND类型的特点可以看前面几节。十四、COM_INTERFACE_ENTRY_NOINTERFACE(x)#define COM_INTERFACE_ENTRY_NOINTERFACE(x)&_ATL_IIDOF(x), NULL, _NoInterface,_NoInterface 是 CComObjectRootBase 的成员函数,看看它的定义:static HRESULT WINAPI

32、_NoInterface(.)return E_NOINTERFACE;原来它只是返回E_NOINTERFACE,并且将终止查询。哈哈,看来是不想让别人查到这个接口啊!十五、 COM_INTERFACE_ENTRY_BREAK(x)#define COM_INTERFACE_ENTRY_BREAK(x)&_ATL_IIDOF(x), NULL, _Break,_Break 也是 CComObjectRootBase 的成员函数,看看它的定义:static HRESULT WINAPI _Break(.)iid;_ATLDUMPIID(iid, _T(Break due to QI for in

33、terface ), S_OK);DebugBreak();return S_FALSE;如果查到这个接口将调用DebugBreak(),并返回S_FALSE,继续查询下去。DebugBreak ()是什 么效果大家自己试试吧,一定很熟悉的,呵呵。至此全部十五个接口映射宏我们都已经讲完了,唉,真是不容易,特别是前面几个宏跟 踪起来很麻烦。因为文本方式的限制,所以很多东西不容易表达清楚。有些叫法也是我自己 这么叫的,可能与别人的习惯不同。没办法,大家将就将就了,呵呵。-全文完-2000/3/31 凌晨补注:关于ATL中类厂的实现问题1. 当创建一个组件时,必须先创建它的类厂,再调用类厂的Crea

34、telnstance()来创建组件.在CComCoClass中定义了宏DECLARE_CLASSFACTORY(),包含了组件的类厂对象._ClassFactoryCreatorClass,它的Createlnstance是用来创建组件的类厂的.也就是 CComCreator ccomobjectcached :CreateInstance();2. 在 CComCoClass 中也定义了宏 DECLARE_AGGREGATABLE(),包含了对象_CreatorClass, 这个对象实际上就是我们要创建的组件对象(具体定义看详解一),它也有一个Crea telns tance,这个函数是用来

35、创建这个组件的!当创建组件的类厂时,会把这个函数的地址告诉给类厂。3. 当我们成功的获得类厂对象后(此时类厂已经创建完毕) ,我们然后将调用类厂的Createlnstance(),在这个函数中,会调用组件的 CreateInstance 从而创建组件。4. 所以,可见这里总共牵扯到三个 CreateInstance:(1) _ClassFactoryCreatorClass:CreateInstance() /用于创建组件的类厂对象(2) CComClassFac tor y:Crea telns tance() /用于调用 _Crea to rClass:Crea telns tance(3) _CreatorClass:CreateInstance() /用于创建组件

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!