ATL Internals 2ed复习.chapter 6.Table driven QueryInterface

来源:互联网 发布:知乎为什么也叫b乎 编辑:程序博客网 时间:2024/06/05 15:25

The Raw Interface Map

ATL实现QueryInterface的方法是调用CComObjectRootBase::InternalQueryInterface():

static HRESULT WINAPI                                         CComObjectRootBase::InternalQueryInterface(                     void*                     pThis,                              const _ATL_INTMAP_ENTRY*  pEntries,                           REFIID                    iid,                                void**                    ppvObject)                        {                                                                 // First entry in the com map should be a simple map entry    ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);            HRESULT hRes = AtlInternalQueryInterface(pThis, pEntries,         iid, ppvObject);                                          return hRes;                                              }                                                             

ATL_INTMAP_ENTRY


其中涉及到ATL_INTMAP_ENTRY,作为数组传递给InternalQueryInterface():

struct _ATL_INTMAP_ENTRY {                                     const IID* piid;       // the interface id (IID)           DWORD_PTR dw;                                              _ATL_CREATORARGFUNC* pFunc; //NULL:end, 1:offset, n:ptr};                                                         


其中_ATL_CREATORARGFUNC的定义为:

typedef HRESULT (WINAPI _ATL_CREATORARGFUNC)(                     void* pv,        // Object's this pointer                     REFIID riid,     // IID of requested interface                LPVOID* ppv,     // Storage for returned interface pointer    DWORD_PTR dw);   // dw from the interface map entry       

AtlInternalQueryInterface

作为一个全局函数,AtlInternalQueryInterface()的作用是遍历interface map,找出匹配的iid,返回指针

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)) { // use first interface                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) { //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);                                if (hRes == S_OK || (!bBlind && FAILED(hRes)))                        return hRes;                                                  }                                                                 }                                                                 pEntries++;                                                   }                                                                 return E_NOINTERFACE;                                         }                                                                 


注意,interface map第一项一定要是_ATL_SIMPLEMAPENTRY类型,否则会assert。

如果查询IUnknown,该函数直接返回第一项

如果pEntries->piid==NULL,将会调用pFunc,在COM_INTERFACE_ENTRY_XXX_BLIND将会用到这一特性。