九、COM_INTERFACE_ENTRY_CHAIN(classname) 参ATL例程COMMAP
先看看它的定义:
典型用法:
它与一般的组件无异。
我们对查询的过程已经很熟悉了,可以直接来看看_Chain的功能。_Chain()是CComObjectRootBase的成员函数:
我们再看看宏定义中的dw部分:
基本上我们已经看懂是怎么回事了,void *p将得到基类的指针,InteralQueryInterface 我们已经很熟悉了,_Chain把基类的指针以及基类的接口映射宏传给它,实际上是查询基类的接口!!!
一般情况下把这个宏放在BEGIN_COM_MAP和END_COM_MAP之间的最后面,这表示只有在当前类中查不到接口时才去查父类的接口。不过也经常把它放在第一位,这时就是先去查父类接口,只有父类没有实现这种接口时才查自己。在ATL中组件是以多重继承的方式实现的,ATL定义了很多类实现了一些常用的接口,这些类经常被做为组件的基类,所以这个宏被大量使用。
所有重要的宏我们都已经讲过了,剩下的都是些很简单的宏了.呵呵,还是把它们都罗列一下,善始善终嘛.
十、COM_INTERFACE_ENTRY_IID(iid, x)
十一、COM_INTERFACE_ENTRY2_IID(iid, x, x2)
从定义上看这两个宏与COM_INTERFACE_ENTRY()和COM_INTERFACE_ENTRY2()相比,都只是多了一项"iid"。没有别的好处,只不过由用户明确指出接口IID,而不用系统根据接口名字去转换了。
十二、COM_INTERFACE_ENTRY_FUNC( iid, dw, func )
还记得AtlInternalQueryInterface()中的代码吗?如果在接口映射表中找到了我们要找的接口,并且这个接口不是_ATL_SIMPLEENTRY型的,则执行宏定义中的指定的函数。
这个宏就给我们提供了自己编写处理函数的功能。这个函数必须是如下定义:HRESULT WINAPI func(void* pv, REFIID riid, LPVOID* ppv, DWORD dw);
当AtlInternalQueryInterface调用func时,会传进相关的信息。pv是类对象的指针,riid是要查询的接口,ppv是要返回查询得到的接口指针,dw是在宏定义中指定的参数。另外如果函数中不打算返回接口指针,则应把ppv赋为NULL,并返回S_FALSE或 E_NOINTERFACE。返回S_FALSE刚会继续查找下去,若返回E_NOINTERFACE则会终止查询。若返回接口指针,则应返回S_OK.
十三、COM_INTERFACE_ENTRY_FUNC_BLIND(dw, func)
至于_BLIND类型的特点可以看前面几节。
十四、COM_INTERFACE_ENTRY_NOINTERFACE(x)
原来它只是返回E_NOINTERFACE,并且将终止查询。
哈哈,看来是不想让别人查到这个接口啊!!!
十五、COM_INTERFACE_ENTRY_BREAK(x)
如果查到这个接口将调用DebugBreak(),并返回S_FALSE,继续查询下去。DebugBreak()是什么效果大家自己试试吧,一定很熟悉的,呵呵。
至此全部十五个接口映射宏我们都已经讲完了,唉,真是不容易,特别是前面几个宏跟踪起来很麻烦。因为文本方式的限制,所以很多东西不容易表达清楚。有些叫法也是我自己这么叫的,可能与别人的习惯不同。没办法,大家将就将就了,呵呵。
先看看它的定义:
| #define COM_INTERFACE_ENTRY_CHAIN(classname)/ {NULL,/ (DWORD)&_CComChainData< classname, _ComMapClass >::data,/ _Chain}, |
典型用法:
| class CChain : public IDispatchImpl< IChain, &IID_IChain, &LIBID_COMMAPLib >, public ISupportErrorInfo, public CComObjectRoot, public CComCoClass< CChain,&CLSID_CChain > { ........ }; |
它与一般的组件无异。
| class COuter : public CChain, .... { 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); return InternalQueryInterface(p, pcd->pFunc(), iid, ppvObject); } struct _ATL_CHAINDATA { DWORD dwOffset; const _ATL_INTMAP_ENTRY* (WINAPI *pFunc)(); }; |
我们再看看宏定义中的dw部分:
| template < class base, class derived > _ATL_CHAINDATA _CComChainData< base, derived >::data = {offsetofclass(base, derived), base::_GetEntries}; |
基本上我们已经看懂是怎么回事了,void *p将得到基类的指针,InteralQueryInterface 我们已经很熟悉了,_Chain把基类的指针以及基类的接口映射宏传给它,实际上是查询基类的接口!!!
一般情况下把这个宏放在BEGIN_COM_MAP和END_COM_MAP之间的最后面,这表示只有在当前类中查不到接口时才去查父类的接口。不过也经常把它放在第一位,这时就是先去查父类接口,只有父类没有实现这种接口时才查自己。在ATL中组件是以多重继承的方式实现的,ATL定义了很多类实现了一些常用的接口,这些类经常被做为组件的基类,所以这个宏被大量使用。
所有重要的宏我们都已经讲过了,剩下的都是些很简单的宏了.呵呵,还是把它们都罗列一下,善始善终嘛.
十、COM_INTERFACE_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()和COM_INTERFACE_ENTRY2()相比,都只是多了一项"iid"。没有别的好处,只不过由用户明确指出接口IID,而不用系统根据接口名字去转换了。
十二、COM_INTERFACE_ENTRY_FUNC( iid, dw, func )
| #define COM_INTERFACE_ENTRY_FUNC(iid, dw, func)/ {&iid, / dw, / func}, |
还记得AtlInternalQueryInterface()中的代码吗?如果在接口映射表中找到了我们要找的接口,并且这个接口不是_ATL_SIMPLEENTRY型的,则执行宏定义中的指定的函数。
这个宏就给我们提供了自己编写处理函数的功能。这个函数必须是如下定义:HRESULT WINAPI func(void* pv, REFIID riid, LPVOID* ppv, DWORD dw);
当AtlInternalQueryInterface调用func时,会传进相关的信息。pv是类对象的指针,riid是要查询的接口,ppv是要返回查询得到的接口指针,dw是在宏定义中指定的参数。另外如果函数中不打算返回接口指针,则应把ppv赋为NULL,并返回S_FALSE或 E_NOINTERFACE。返回S_FALSE刚会继续查找下去,若返回E_NOINTERFACE则会终止查询。若返回接口指针,则应返回S_OK.
十三、COM_INTERFACE_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 _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 interface "), S_OK); DebugBreak(); return S_FALSE; } |
如果查到这个接口将调用DebugBreak(),并返回S_FALSE,继续查询下去。DebugBreak()是什么效果大家自己试试吧,一定很熟悉的,呵呵。
至此全部十五个接口映射宏我们都已经讲完了,唉,真是不容易,特别是前面几个宏跟踪起来很麻烦。因为文本方式的限制,所以很多东西不容易表达清楚。有些叫法也是我自己这么叫的,可能与别人的习惯不同。没办法,大家将就将就了,呵呵。
本文详细介绍了ATL中的COM_INTERFACE_ENTRY_CHAIN宏及其作用,它用于查询基类接口。此外,还列举并解释了COM_INTERFACE_ENTRY_IID、COM_INTERFACE_ENTRY2_IID、COM_INTERFACE_ENTRY_FUNC、COM_INTERFACE_ENTRY_FUNC_BLIND、COM_INTERFACE_ENTRY_NOINTERFACE和COM_INTERFACE_ENTRY_BREAK等宏的功能和用法,帮助理解ATL组件接口映射的机制。

9685

被折叠的 条评论
为什么被折叠?



