设为首页 加入收藏

TOP

ATL接口映射宏详解(五)
2012-11-04 15:22:38 来源: 作者: 【 】 浏览:1239
Tags:ATL 接口 映射 详解
  六、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()是什么效果大家自己试试吧,一定很熟悉的。

  至此全部十五个接口映射宏我们都已经讲完了,唉,真是不容易,特别是前面几个宏跟踪起来很麻烦。因为文本方式的限制,所以很多东西不容易表达清楚。有些叫法也是我自己这么叫的,可能与别人的习惯不同。没办法,大家将就将就了。

首页 上一页 2 3 4 5 6 7 下一页 尾页 5/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇基于VC实现Java和Matlab的通信 下一篇用ATL实现VC插件

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: