Статья:
Как подключиться к событиям СOM-объекта на С++Автор(ы): Владислав Чистяков
Авторы:
Владислав Чистяков
Аннотация:
Часто при использовании COM-объектов в С++-программах встает необходимость подключения к их событиям. Если вы используете MFC, VCL или другие высокоуровневые библиотеки классов, проблем не возникает, так как для решения этой задачи существуют «мастера» и т.п. Но когда такая проблема возникает при работе на уровне API или при использовании ATL, многие программисты робеют, поскольку это связано с написанием больших объемов нетривиального кода, а это неизбежно таит множество подводных камней. В ATL 3.0 господа из Microsoft ввели кусок кода, красиво и лаконично решающий эту проблему. Однако в этот момент отдел маркетинга Microsoft был сильно занят рекламой очень нужных (с его точки зрения) технологий, а на эту мелочь у него у него денег не нашлось. Недавно в конференции microsoft.public.ru.vc был задан вопрос, натолкнувший меня на размышления. Он звучал так: «Как подключиться к connection point'ам на С++ без использования MFC?». Сам вопрос ничего странного не содержит, чего не скажешь об ответах. В них надменно говорилось: «...создаешь disp-интерфейс, делаешь Advise...», и в конце тихонько упоминалось, что Advise можно упростить с помощью вызова AtlAdvise. Стало ясно, что нужно как-то осветить этот вопрос.
К сожалению, в действительности все выглядит иначе, чем на самом деле. Итак, по порядку:
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.
Ну вот и все.
1. Тут ты ошибаешся. Наследование от IDispEventSimpleImpl или IDispEventImpl уже прводит к тому что твой объект становится COM-объектом (реализует QI, AddRef, ...).
2. Да похоже проблемы есть, хотя я на них не натыкался. Но не проще ли пропатчить ATL? В
Q241810 сказано что нужно делат...
3. Может быть и действительно делать не обязательно, так как это делает IDispEventXxxImpl, но предпослки не верные.
4. Наверно действительно нужно было упамянуть про IDispEventSimpleImpl, но так как IDispEventImpl более удобен...
5. Затм, что импорт вредная вещь в ATL-проектах. Иной раз тяжело избавиться от той грязи которую он вност. Тем более что количество операций которое нужно сделать чтобы выдрсть описание из tlh намного больше чем открыть меню "Tools@" в VC IDE выбрать нужую библиотеку и скопировать описание. Еще это более целесообразно по тому, что стиль генерируемого кода у OLE/COM-Veiw-ера больше подходт нежели tlh.
6. Для подключения к событиям в диалогах проще пользоваться визардом о чем и сказано в статье.