开发者

Why does ATL COM map scanning code expect the first entry to be of _ATL_SIMPLEMAPENTRY type?

ATL provides a bunch of macros for creating so-called COM maps - chains of rules of how the QueryInterface() call behaves on a given object. The map begins with BEGIN_COM_MAP and ends with END_COM_MAP. In between the the following can be used (among others):

  • COM_INTERFACE_ENTRY, COM_INTERFACE_ENTRY2 - to ask C++ to simply cast this class to the corresponding COM interface
  • COM_INTERFACE_ENTRY_FUNC - to ask C++ to call a function that will retrieve the interface

Now the problem is I want to use COM_INTERFACE_ENTRY_FUNC for every interface I expose so that I can log all the calls - I believe it will help me debugging my component when it is deployed in the field. The implementation of CComObjectRootBase::InternalQueryInterface contains an ATLASSERT:

ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);

which implies that the following is allright:

BEGIN_COM_MAP
    C开发者_JS百科OM_INTERFACE_ENTRY( IMyInterface1 )
    COM_INTERFACE_ENTRY_FUNC( __uuidof(IMyInterface2), 0, OnQueryMyInterface2 )
END_COM_MAP

since here the first entry results in _ATL_SIMPLEMAPENTRY type entry but the following is not:

BEGIN_COM_MAP
    COM_INTERFACE_ENTRY_FUNC( __uuidof(IMyInterface1), 0, OnQueryMyInterface1 )
    COM_INTERFACE_ENTRY_FUNC( __uuidof(IMyInterface2), 0, OnQueryMyInterface2 )
END_COM_MAP

since here the entry type will not be _ATL_SIMPLEMAPENTRY.

This makes no sense at all. Why am I enforced into having a "please, C++, do the static_cast" entry as the first entry of the COM map?

Upd: Resolved after many more hour of debugging, answer added.


Inside ATL there's AtlInternalQueryInterface() that actually does scan the COM map. Inside it there's this code:

if (InlineIsEqualUnknown(iid)) // use first interface
{
    IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);
    // call AddRef on pUnk, copy it to ppvObject, return S_OK
}

this code actually relies on the first entry of the table being of _ATL_SIMPLEMAPENTRY type since it expects that _ATL_INTMAP_ENTRY::dw stores an offset from the current object this pointer to the necessary interface. In the use cited in the question if the first entry is this one:

COM_INTERFACE_ENTRY_FUNC( __uuidof(IMyInterface1), 0, OnQueryMyInterface1 )

the entry will be of wrong type, but the _ATL_INTMAP_ENTRY::dw will be zero (second parameter to the macro) and the code will happily work each time returning this pointer as IUnknown*. But if the second macro parameter which corresponds to a pass this value into the function specified as the third parameter variable is not zero the program will use that value as the offset and could run into undefined behaviour.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜