前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ATL源码学习3---接口的查询支持

ATL源码学习3---接口的查询支持

作者头像
雪影
发布2018-08-02 14:27:10
6990
发布2018-08-02 14:27:10
举报
文章被收录于专栏:流媒体人生流媒体人生

1.    ATL的QueryInterface调用追踪

a. 组件的QueryInterface函数定义

代码语言:javascript
复制
[cpp] view plain copy
template <class Base>  
class CComObject : public Base  
{  
    STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) throw()  //QueryInterface函数 
    {return _InternalQueryInterface(iid, ppvObject);}  
}  

b. _InternalQueryInterface函数调用InternalQueryInterface函数,定义在BEGIN_COM_MAP宏内部

#define BEGIN_COM_MAP(x) public: /     typedef x _ComMapClass; /     static HRESULT WINAPI _Cache(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw) throw()/     {/         _ComMapClass* p = (_ComMapClass*)pv;/         p->Lock();/         HRESULT hRes = E_FAIL; /         __try /         { /             hRes = ATL::CComObjectRootBase::_Cache(pv, iid, ppvObject, dw);/         } /         __finally /         { /             p->Unlock();/         } /         return hRes;/     }/     IUnknown* _GetRawUnknown() throw() /     { ATLASSERT(_GetEntries()[0].pFunc == _ATL_SIMPLEMAPENTRY); return (IUnknown*)((INT_PTR)this+_GetEntries()->dw); } /     _ATL_DECLARE_GET_UNKNOWN(x)/ HRESULT _InternalQueryInterface(REFIID iid, void** ppvObject) throw() /     { return InternalQueryInterface(this, _GetEntries(), iid, ppvObject); } /     const static ATL::_ATL_INTMAP_ENTRY* WINAPI _GetEntries() throw() { /     static const ATL::_ATL_INTMAP_ENTRY _entries[] = { DEBUG_QI_ENTRY(x)

c. InternalQueryInterface函数定义在CComObjectRootBase类中(这里显示的代码已经删除了调试扩展的内容)。

代码语言:javascript
复制
[cpp] view plain copy
static HRESULT WINAPI InternalQueryInterface(void* pThis,  
 const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)  
{  
 HRESULT hRes = AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject);  
 return hRes;  
}  

InternalQueryInterface委派全局函数AtlInternalQueryInterface来提供接口查询的实现。在查询接口之前,先检查查询的接口IID,如果请求的是IUnknown,从表中取出第一个表项立即返回,不需要偏历表的剩余部分。     关于表的遍历,对于表中的每个表项,根据指向表项接口标识符的piid成员是否为NULL。     1. 如果不为NULL,表项IID与请求IID进行比较,如果匹配,pFunc引用的函数被调用,结果返回客户。如果不匹配,进入下一个表现搜索。     2. 如果piid为NULL,则不管请求的IID是什么,都会调用pFunc。如果接口是S_OK,则返回结果给客户。否则继续搜索下一个表项。所有的COM_INTERFACE_ENTRY_XXX_BLIND宏都使用了这种行为。比如COM_INTERFACE_ENTRY_AGGREGATE_BLIND。

代码语言:javascript
复制
[cpp] view plain copy
///////////////////////////////////////////////////////////////////////////// 
// QI support 
ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis,  
 const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)  
{  
    ATLASSERT(pThis != NULL);  
    ATLASSERT(pEntries!= NULL);  
 if(pThis == NULL || pEntries == NULL)  
 return E_INVALIDARG ;  
 // First entry in the com map should be a simple map entry 
    ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);  
 if (ppvObject == NULL)  
 return E_POINTER;  
    *ppvObject = NULL;  
 if (InlineIsEqualUnknown(iid)) // 请求的是IUnknown接口 
    {  
            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))//piid是否与iid相等 
        {  
 if (pEntries->pFunc == _ATL_SIMPLEMAPENTRY) //offset 
            {  
                ATLASSERT(!bBlind);  
                IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);  
                pUnk->AddRef();  
                *ppvObject = pUnk;  
 return S_OK;  
            }  
 else //actual function call 
            {  
 HRESULT hRes = pEntries->pFunc(pThis,  
                    iid, ppvObject, pEntries->dw);   
 //常用的pFunc函数在CComObjectRootBase函数中有定义, 
 //包括_Creator、_Delegate、_ChainAttr、_Cache、_Break、_NoInterface 
 if (hRes == S_OK || (!bBlind && FAILED(hRes)))  
 return hRes;  
            }  
        }  
        pEntries++;  
    }  
 return E_NOINTERFACE;  
}  

2.接口映射表的源码分析 通过上面的函数调用追踪,我们可以发现函数最终是遍历_ATL_INTMAP_ENTRY数组结构。关于_ATL_INTMAP_ENTRY数组的定义在BEGIN_COM_MAP宏内部。是一个静态数据变量。

代码语言:javascript
复制
[cpp] view plain copy
HRESULT _InternalQueryInterface(REFIID iid, void** ppvObject) throw() /  
{ return InternalQueryInterface(this, _GetEntries(), iid, ppvObject); } /  
const static ATL::_ATL_INTMAP_ENTRY* WINAPI _GetEntries() throw() { /  
static const ATL::_ATL_INTMAP_ENTRY _entries[] = { DEBUG_QI_ENTRY(x)  
        ......  
        ......  
__if_exists(_GetAttrEntries) {{NULL, (DWORD_PTR)_GetAttrEntries, _ChainAttr }, }/  
{NULL, 0, 0}}; /  
return &_entries[1];}   

下面我们来看看_ATL_INTMAP_ENTRY结构的定义

代码语言:javascript
复制
[cpp] view plain copy
struct _ATL_INTMAP_ENTRY  
{  
 const IID* piid;       // 接口的IID 
 DWORD_PTR dw;  
    _ATL_CREATORARGFUNC* pFunc; //NULL:end, 1:offset, n:ptr 
};  

_ATL_CREATORARGFUNC函数类型的定义

代码语言:javascript
复制
[cpp] view plain copy
typedef HRESULT (WINAPI _ATL_CREATORARGFUNC)(void* pv, //对象的this指针 
    REFIID riid, //请求的接口IID 
 LPVOID* ppv, //存储返回的结构指针 
 DWORD_PTR dw);//来源于接口映射表项的dw 

3.常用的 pFunc 函数的定义

常用的pFunc函数在CComObjectRootBase函数中有定义,其中包括_Creator、_Delegate、_ChainAttr、_Cache、_Break、_NoInterface。

代码语言:javascript
复制
[cpp] view plain copy
//1.函数功能说明:_Break函数主要用于调试,追查问题的所在 
 //2.相关的宏:   COM_INTERFACE_ENTRY_BREAK(x) 
 //      {&_ATL_IIDOF(x), NULL, _Break}, 
 static HRESULT WINAPI _Break(void* /* pv */, REFIID iid, void** /* ppvObject */, DWORD_PTR /* dw */)  
    {  
        (iid);  
        _ATLDUMPIID(iid, _T("Break due to QI for interface "), S_OK);  
        DebugBreak();  
 return S_FALSE;  
    }  
 //1.函数功能说明:_NoInterface函数主要用于屏蔽指定的接口,例如当盲目聚合某些组件时,可能需要屏蔽一些接口 
 //2.相关的宏:   COM_INTERFACE_ENTRY_NOINTERFACE(x) 
 //      {&_ATL_IIDOF(x), NULL, _NoInterface}, 
 static HRESULT WINAPI _NoInterface(void* /* pv */, REFIID /* iid */, void** /* ppvObject */, DWORD_PTR /* dw */)  
    {  
 return E_NOINTERFACE;  
    }  
 //1.函数功能说明:_Creator主要用于Tear-off技术中,用于创建子对象组件 
 //2.相关的宏:   COM_INTERFACE_ENTRY_TEAR_OFF(iid, x) 
 //      {&iid,(DWORD_PTR)&ATL::_CComCreatorData</ 
 //      ATL::CComInternalCreator< ATL::CComTearOffObject< x > >>::data,  _Creator}, 
 static HRESULT WINAPI _Creator(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw)  
    {  
        _ATL_CREATORDATA* pcd = (_ATL_CREATORDATA*)dw;  
 return pcd->pFunc(pv, iid, ppvObject);  
    }  
 //1.函数功能说明:_Cache主要用于Tear-off和聚合技术中,除了有创建内部组件对象功能外,还要将内部对象组件的指针保存 
 //2.相关的宏:   a. COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(iid, x, punk)    缓存tear-off 
 //          {&iid,(DWORD_PTR)&ATL::_CComCacheData<ATL::CComCreator< ATL::CComCachedTearOffObject< x > >,/ 
 //          (DWORD_PTR)offsetof(_ComMapClass, punk)>::data,_Cache},  // 
 //      b. COM_INTERFACE_ENTRY_AUTOAGGREGATE(iid, punk, clsid)   
 //          {&iid,(DWORD_PTR)&ATL::_CComCacheData<ATL::CComAggregateCreator<_ComMapClass, &clsid>,/ 
 //          (DWORD_PTR)offsetof(_ComMapClass, punk)>::data,_Cache},   
 //      c.COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(punk, clsid) 
 //          {NULL,(DWORD_PTR)&ATL::_CComCacheData<ATL::CComAggregateCreator<_ComMapClass, &clsid>,/ 
 //          (DWORD_PTR)offsetof(_ComMapClass, punk)>::data,_Cache}, 
 static HRESULT WINAPI _Cache(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw)  
    {  
 HRESULT hRes = E_NOINTERFACE;  
        _ATL_CACHEDATA* pcd = (_ATL_CACHEDATA*)dw;  
        IUnknown** pp = (IUnknown**)((DWORD_PTR)pv + pcd->dwOffsetVar);  
 if (*pp == NULL)  
            hRes = pcd->pFunc(pv, __uuidof(IUnknown), (void**)pp);  
 if (*pp != NULL)  
            hRes = (*pp)->QueryInterface(iid, ppvObject);  
 return hRes;  
    }  
 //1.函数功能说明:_Delegate主要用于聚合技术中, 
 //        和_Cache主要区别是需要客户手动创建内部组件对象,一般在FinalConstruct中创建内部对象 
 //2.相关的宏:   a. COM_INTERFACE_ENTRY_AGGREGATE(iid, punk) 
 //            {&iid,(DWORD_PTR)offsetof(_ComMapClass, punk),_Delegate} 
 //      b. COM_INTERFACE_ENTRY_AGGREGATE_BLIND(punk) 
 //            {NULL,(DWORD_PTR)offsetof(_ComMapClass, punk),_Delegate}, 
 static HRESULT WINAPI _Delegate(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw)  
    {  
 HRESULT hRes = E_NOINTERFACE;  
        IUnknown* p = *(IUnknown**)((DWORD_PTR)pv + dw);  
 if (p != NULL)  
            hRes = p->QueryInterface(iid, ppvObject);  
 return hRes;  
    }  
 //1.函数功能说明:_Chain主要用于继承基类的映射链表 
 //      从一个自己提供了接口映射表的基类继承时,在派生类的接口映射表中避免重复的表项,方便维护 
 //2.相关的宏:   COM_INTERFACE_ENTRY_CHAIN(classname)/ 
 //          {NULL,(DWORD_PTR)&ATL::_CComChainData<classname, _ComMapClass>::data,_Chain}, 
 static HRESULT WINAPI _Chain(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw)  
    {  
        _ATL_CHAINDATA* pcd = (_ATL_CHAINDATA*)dw;  
 void* p = (void*)((DWORD_PTR)pv + pcd->dwOffset);  
 return InternalQueryInterface(p, pcd->pFunc(), iid, ppvObject);  
    }  
 static HRESULT WINAPI _ChainAttr(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw)  
    {  
 const _ATL_INTMAP_ENTRY* (WINAPI *pFunc)() = (const _ATL_INTMAP_ENTRY* (WINAPI *)())dw;  
 const _ATL_INTMAP_ENTRY *pEntries = pFunc();  
 if (pEntries == NULL)  
 return S_OK;  
 return InternalQueryInterface(pv, pEntries, iid, ppvObject);  
    }  

4.ATL接口的查询的扩展 #define COM_INTERFACE_ENTRY_FUNC(iid, dw, func)/     {&iid, dw, func}, #define COM_INTERFACE_ENTRY_FUNC_BLIND(dw, func)/     {NULL, dw,func}, 这两个宏其实是ATL的QueryInterface实现的通用后门,用户可以自定义func,在func函数中暴露COM接口,但需要遵守COM实体身份规则。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2009年09月22日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档