К сожалению, в действительности все выглядит иначе, чем на самом деле. Итак, по порядку:
1. «Берете любой класс, реализованный как COM-объект». На самом деле можно использовать любой класс. Поскольку COM-объекты обычно используются в клиентском приложении, то создание своего COM класса может быть нежелательным. Это может быть класс фрейма, диалога или просто производный от IDispEventImpl или IDispEventSimpleImpl.
2. «Этот класс нужно унаследовать от IDispEventImpl». Проблема в том, что в реализации IDispEventImpl имеются баги, не устраненные в VC6.0 SP5 (Q237771,
Q241810, Q244204). В последнием случае трудно найти причину – все реализовано правильно, сообщений об ошибках нет, а события не вызываются. Поэтому фактически можно использовать только IDispEventSimpleImpl, который отличается тем, что он не использует Type Library для получения информации о вызываемом методе.
3. «Добавляете к COM-карте ваш событийный интерфейс». Нет COM-класса – нет и COM-карты.
4. «Добавляете Sink-карту». В случае IDispEventSimpleImpl вместо SINK_ENTRY_EX нужно использовать макрос SINK_ENTRY_INFO и описать метод в структуре ATL_FUNC_INFO (вне класса):
static _ATL_FUNC_INFO OnDblClickInfo = {
CC_STDCALL, // Calling convention.
VT_EMPTY, // Return type.
0, // Number of arguments.
NULL // Argument types.
};
BEGIN_SINK_MAP(CEventSink)
SINK_ENTRY_INFO(0, DIID__ICtrlEvents, DISPID_DBLCLICK, OnDblClick, &OnDblClickInfo)
END_SINK_MAP()
Хорошо бы еще упомянуть, что идентификатор объекта (в примере — 0) должен соответствовать номеру в объявлении класса. В случае использования объекта в диалоге должен использоваться идентификатор в диалоге.
5. «Залезаете в OLE/COM Object Viewer» – зачем так далеко? По директиве #import генерируется .tlh файл – там все написано.
6. Для подключения и отключения источника событий в диалоге удобнее использовать функцию AtlAdviseSinkMap.
Ну вот и все.