Почему код сканирования карты ATL COM ожидает, что первая запись будет иметь тип _ATL_SIMPLEMAPENTRY? - PullRequest
3 голосов
/ 16 ноября 2009

ATL предоставляет набор макросов для создания так называемых COM-карт - цепочек правил поведения вызова QueryInterface () для данного объекта. Карта начинается с BEGIN_COM_MAP и заканчивается END_COM_MAP. В промежутке между ними может использоваться следующее (среди прочих):

  • COM_INTERFACE_ENTRY, COM_INTERFACE_ENTRY2 - попросить C ++ просто привести этот класс к соответствующему интерфейсу COM
  • COM_INTERFACE_ENTRY_FUNC - попросить C ++ вызвать функцию, которая будет получать интерфейс

Теперь проблема в том, что я хочу использовать COM_INTERFACE_ENTRY_FUNC для каждого интерфейса, который я выставляю, чтобы я мог регистрировать все вызовы - я верю, что это поможет мне отладить мой компонент, когда он развернут в поле. Реализация CComObjectRootBase :: InternalQueryInterface содержит ATLASSERT:

ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);

, что означает, что все в порядке:

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

, поскольку здесь первая запись приводит к записи типа _ATL_SIMPLEMAPENTRY, но следующее не является:

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

, поскольку здесь тип записи не будет _ATL_SIMPLEMAPENTRY.

Это не имеет никакого смысла вообще. Почему я вынужден использовать запись «пожалуйста, C ++, сделать static_cast» в качестве первой записи карты COM?

Upd: Исправлено после долгих часов отладки, добавлен ответ.

1 Ответ

2 голосов
/ 17 ноября 2009

Внутри ATL есть AtlInternalQueryInterface (), которая фактически сканирует карту COM. Внутри этого есть код:

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
}

этот код фактически опирается на первую запись таблицы, имеющую тип _ATL_SIMPLEMAPENTRY, поскольку ожидает, что _ATL_INTMAP_ENTRY :: dw хранит смещение от текущего объекта этого указателя на необходимый интерфейс. При использовании, процитированном в вопросе, является ли первая запись этой:

COM_INTERFACE_ENTRY_FUNC( __uuidof(IMyInterface1), 0, OnQueryMyInterface1 )

запись будет неправильного типа, но _ATL_INTMAP_ENTRY :: dw будет нулевым (второй параметр макроса), и код будет успешно работать каждый раз, возвращая этот указатель как IUnknown *. Но если второй параметр макроса, который соответствует , передает это значение в функцию, указанную в качестве переменной третьего параметра, отличной от нуля, программа будет использовать это значение в качестве смещения и может столкнуться с неопределенным поведением.

...