六、COM_INTERFACE_ENTRY_AGGREGATE_BLIND 参ATL例程COMMAP 上一节我们讲了COM_INTERFACE_ENTRY_AGGREGATE,这节要介绍的宏与它很类似。
#define COM_INTERFACE_ENTRY_AGGREGATE_BLIND(punk)\
{NULL,\
(DWORD)offsetof(_ComMapClass, punk),\
_Delegate},
从定义上就可以看出,它与上一节介绍宏的唯一区别就在于,它没有指明接口ID!!所以在它的定义中第一项也是NULL。
这个宏的用法与我们COM_INTERFACE_ENTRY_AGGREGATE一模一样。大家可以参考上一节内容以及ATL的例程COMMAP。
我们来看看AtlInternalQueryInterface()中的相关代码。
ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis, const _ATL_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) //offset { ATLASSERT(!bBlind); IUnknown* pUnk = (IUnknown*)((int)pThis+pEntries->dw); pUnk->AddRef(); *ppvObject = pUnk; return S_OK; } else { HRESULT 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,也会执行
_Delegate.
从上可见,这个宏适用于一个聚集组件有多个接口的情况,这样只要是查询这个聚集组件的接口,就会进入_Delegate函数。但要特别注意的是这个宏的位置!!
比如若是这样的顺序:
BEGIN_COM_MAP COM_INTERFACE_ENTRY_AGGREGATE_BLIND(m_pUnkAggBlind.p) COM_INTERFACE_ENTRY(IOuter) END_COM_MAP |
当查询IOuter接口时就会出错!!!
七、COM_INTERFACE_ENTRY_AUTOAGGREGATE(iid, punk, clsid) 参ATL例程COMMAP
先看看这个宏的定义:
#define COM_INTERFACE_ENTRY_AUTOAGGREGATE(iid, punk, clsid)\ {&iid,\ (DWORD)&_CComCacheData<\ CComAggregateCreator<_ComMapClass, &clsid>,\ (DWORD)offsetof(_ComMapClass, punk)\ >::data,\ _Cache}, |
先看看它的典型用法:
class CAutoAgg : public IDispatchImpl, public ISupportErrorInfo, public CComObjectRoot, public CComCoClass { ...... ...... }; |
与一般的组件并无二样。
class COuter : public CChainBase, public IDispatchImpl, public CComCoClass { BEGIN_COM_MAP(COuter) COM_INTERFACE_ENTRY_AUTOAGGREGATE(IID_IAutoAgg, m_pUnkAutoAgg.p, CLSID_CAutoAgg) END_COM_MAP() CComPtr m_pUnkAutoAgg; }; |
与宏COM_INTERFACE_ENTRY_AGGREGRATE(_)不同,COuter不用在FinalConstruct中创建聚集组件。外部组件会自动创建聚集组件!!!
1。
template _ATL_CACHEDATA _CComCacheData::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_IUnknown, (void**)pp); if (*pp != NULL) hRes = (*pp)->QueryInterface(iid, ppvObject); return hRes; } |
3。
template class CComAggregateCreator { public: static HRESULT WINAPI CreateInstance(void* pv, REFIID/*riid*/, LPVOID* ppv ) { ATLASSERT(*ppv == NULL); ATLASSERT(pv != NULL); T* p = (T*) pv; return CoCreateInstance(*pclsid, p->GetControllingUnknown(), CLSCTX_INPROC, IID_IUnknown, ppv); } }; |
因为_Cache,_CComCacheData,CComAggregateCreator这几个类和函数我们已经在前面见过或者见过类似的,所以就不再多讲了。总之我们可以看到,若m_pUnkAutoAgg.p不为空则直接查询,否则创建聚集组件。
与宏COM_INTERFACE_ENTRY_AGGREGATE相比,这个宏似乎更好一些,仅当需要时才会创建使用更简单。
八、COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND( punk, clsid ) 参ATL例程COMMAP
看看它的定义:
#define COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(punk, clsid)\ {NULL,\ (DWORD)&_CComCacheData<\ CComAggregateCreator<_ComMapClass, &clsid>,\ (DWORD)offsetof(_ComMapClass, punk)\ >::data,\ _Cache}, |
这个宏综合了COM_INTERFACE_ENTRY_AUTOAGGREGATE()和COM_INTERFACE_ENTRY_AGGREGATE_BLIND()的特点,既可以自动创建也可以很方便地查询聚集组件中的多个接口。不再赘述!!
九、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 : 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
_ATL_CHAINDATA _CComChainData::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刚会继续查找下去,若返回_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()是什么效果大家自己试试吧,一定很熟悉的。
至此全部十五个接口映射宏我们都已经讲完了,唉,真是不容易,特别是前面几个宏跟踪起来很麻烦。因为文本方式的限制,所以很多东西不容易表达清楚。有些叫法也是我自己这么叫的,可能与别人的习惯不同。没办法,大家将就将就了。