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将会用到这一特性。