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对象中必须首先存在一个包含其所有实现接口的接口映射表。
我想,下面应该去看看《接口映射表是怎样建成的》了。
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对象中必须首先存在一个包含其所有实现接口的接口映射表。
我想,下面应该去看看《接口映射表是怎样建成的》了。
相关文章推荐
- ATL学习笔记(3): QueryInterface功能的实现
- PHP学习笔记 2009-8-25 实现分页显示功能
- ITCAST视频-Spring学习笔记(使用CGLIB实现AOP功能与AOP概念解释)
- 【黑马程序员】简单拍照功能的实现(学习笔记)之一
- Java菜鸟学习笔记--语法篇(一):用Math.random()实现验证码功能
- ASP.NET学习笔记(5)--"返回上一页"功能的实现
- android 学习笔记3--静默安装功能的实现
- 【黑马程序员】简单拍照功能的实现(学习笔记)之二
- 学习笔记:GDI,截图功能实现
- 研读asp.net排课功能实现学习笔记
- MonoRail学习笔记五:定制服务实现自定义功能
- ExtJS学习笔记八,复杂页面功能实现
- [Silverlight学习笔记]实现上传图片功能时遇到的问题
- JQuery学习笔记 [Ajax实现新闻点评功能] (6-3)
- jQuery学习笔记(3)--用jquery(插件)实现多选项卡功能
- MonoRail学习笔记五:定制服务实现自定义功能
- [学习笔记]小型论坛功能——实现按照指定每页的行数来分页显示记录[3]
- jsp学习笔记2——分页功能实现
- 学习笔记二:checkbox实现全选功能
- Java菜鸟学习笔记(5)--用Math.random()实现验证码功能