您的位置:首页 > 产品设计 > UI/UE

ATL学习笔记(3): QueryInterface功能的实现

2014-06-04 11:37 232 查看
在CComObjectRootEx类中,实现了线程安全的引用计数管理。而在CComObjectRootEx的父类CComObjectRootBase中,存在对QueryInterface的一个内部实现——InternalQueryface()。

1. CComObjectRootBase类

class CComObjectRootBase

{

public:

......

static HRESULT InternalQueryInterface(

void* pThis, const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObj)

{ return hRes = AtlInternalQueryInterface(pThis, pEntries, iid, ppvObj); }

......

};

以上代码作了一些简化,去掉了调试用的条件编译选项。在代码中,InternalQueryInterface作为静态方法存在,并且仅仅将执行过程转给了AtlInternalQueryInterface()。其方法参数与标准的QueryInterface方法相比也有不同,其中也有一个新的结构类型_ATL_INTMAP_ENTRY,并且InternalQueryInterface和AtlInternalQueryInterface函数的参数一一对应。

2. _ATL_INTMAP_ENTRY结构和AtlInternalQueryInterface()函数

在atlbase.h中,_ATL_INTMAP_ENTRY结构类型有以下声明:

struct _ATL_INTMAP_ENTRY

{

const IID * piid;

DWORD_PTR dw;

_ATL_CREATORARGFUNC* pFunc;

};

其中piid表示接口的ID,其他的成员变量说明现在暂时无从知晓,只知_ATL_CREATORARGFUNC在atlbase.h中的声明是:

typedef HRESULT (WINAPI _ATL_CREATORARGFUNC) (

void* pv, REFIID riid, LPVOID* ppv, DWORD_PTR dw);

同样,这个函数类型在MSDN中也没有找到说明。

跳过这些暂时无法弄清的声明,看看AtlInternalQueryInterface()函数的实现:

ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis,

const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)

{

if (InlineIsEqualUnknown(iid))

{

IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);

pUnk->AddRef();

*ppvObject = pUnk;

return S_OK;

}

while (pEntries->pFunc != NULL)

{

BOOL bBlind = (pEntries->piid == NULL);

if (bBlind || InlineIsEqualGUID(*(pEntries->piid), iid))

{

if (pEntries->pFunc == _ATL_SIMPLEMAPENTRY)

{

IUnknown* pUnk = (IUnknown*)((INT_PTR)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;

}

参考MSDN,得知:

pThis - 指向包含有COM接口映射表的对象的指针;

pEntries - 一个_ATL_INTMAP_ENTRY结构类型的数组,该数组中包含有效的接口映射;

iid - 请求的接口的GUID;

ppvObj - 用以输出指向iid表示的接口的指针。

参照AtlInternalQueryInterface的实现,如果参数中的iid与IUnknown接口的iid相同(InlineIsEqualUnknown(iid))),将直接输出对象指针加上_ATL_INTMAP_ENTRY结构数组的第一个元素的dw成员的偏移。可见,在请求IUnknown时,函数返回的时pEntries数组中第一个元素表示的接口的IUnknown实现(因为每个COM接口都从IUnknown实现,在多重继承的前提下,函数只返回第一个接口的IUnknown)。

如果请求的接口不是IUnknown,则AtlInternalQueryInterface将继续搜索pEntries数组,直到存在一个元素,并且该元素的pFunc指针是NULL。可见,pEntries数组应该包含pThis所指向的COM对象所实现的所有接口,即所谓的COM对象接口映射表。并且,该数组中最后一个元素不表示任何接口且其pFunc成员应为NULL。

同时还可知,如果接口映射表元素的pFunc成员值为_ATL_SIMPLEMAPENTRY,dw成员则表示所对应的接口指针相对于pThis对象指针的偏移。否则pFunc指向一个自定义的接口指针计算函数。

3. 结论

根据上述分析,与AddRef和Release相似,ATL也没有直接实现IUnknown的QueryInterface方法,而同样是在CComObjectBase类中先作一个内部实现,该实现随着CComObjectRootEx被继承到每个COM对象中。

ATL对于QueryInterface的实现采用的是表驱动的方式(MFC也常用到表驱动方式,似乎是Microsoft钟情于表驱动这个方式,也可能这种方式的确在性能上有过人之处),因此每个ATL COM对象中必须首先存在一个包含其所有实现接口的接口映射表。

我想,下面应该去看看《接口映射表是怎样建成的》了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: