Часто при использовании COM-объектов в С++-программах встает необходимость подключения к их событиям. Если вы используете MFC, VCL или другие высокоуровневые библиотеки классов, проблем не возникает, так как для решения этой задачи существуют «мастера» и т.п. Но когда такая проблема возникает при работе на уровне API или при использовании ATL, многие программисты робеют, поскольку это связано с написанием больших объемов нетривиального кода, а это неизбежно таит множество подводных камней. В ATL 3.0 господа из Microsoft ввели кусок кода, красиво и лаконично решающий эту проблему. Однако в этот момент отдел маркетинга Microsoft был сильно занят рекламой очень нужных (с его точки зрения) технологий, а на эту мелочь у него у него денег не нашлось. Недавно в конференции microsoft.public.ru.vc был задан вопрос, натолкнувший меня на размышления. Он звучал так: «Как подключиться к connection point'ам на С++ без использования MFC?». Сам вопрос ничего странного не содержит, чего не скажешь об ответах. В них надменно говорилось: «...создаешь disp-интерфейс, делаешь Advise...», и в конце тихонько упоминалось, что Advise можно упростить с помощью вызова AtlAdvise. Стало ясно, что нужно как-то осветить этот вопрос.
Упростить подключение к событиям можно с помощью IDispEventImpl (см. MSDN). Если ваш ActiveX был помещен в ATL-диалог во время разработки, то просто выберите из контекстного меню пункт Events ..., выделите ваш ActiveX и добавляйте обработчики. В случае невизуального ActiveX-элемента придется создать пару строк вручную.
Берете любой класс, реализованный как COM-объект. Главное, чтобы он был унаследован от CComObjectRoot и от CComCoClass. Для создания нового класса проще всего использовать ATL-wizard, в котором выбрать «Simple Object».Проще всего вызвать ATL-wizard и выбрать "Simple Object".
Этот класс нужно унаследовать от IDispEventImpl. Например:
class ATL_NO_VTABLE CCntrDispEvents :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CCntrDispEvents>,
public IDispEventImpl<0, CCntrDispEvents, &DIID__IascContainerCtrlEvents, &LIBID_ASCCONTAINERLib, 1, 0>
{ ...
DISPID_XXX – DISPID метода из событийного интерфейса;
SomeEventHandler – имя метода обработчика события.
Залезаете в "OLE/COM Object Viewer" (меню Tools), находите описание COM-объекта, к событиям которого нужно подключиться. Находите описание событийного интерфейса и копируете в буфер описание нужного метода.
Возвращаетесь в VC и вставляете скопированное описание внутрь описания класса. В общем, создаете метод с правильными параметрами.
Создаете экземпляр класса (если он еще не создан):
CComObject<CCntrDispEvents>::CreateInstance(&m_pCntrDispEvents);
m_pCntrDispEvents->AddRef();
// AddRef нужен, так как у объекта, // созданного таким образом, счетчик равен нулю, и он // самоуничтожится при попытке подключения
Впервые статья была опубликована в журнале
<Технология Клиент-Сервер>.
Эту и множество других статей по программированию, разработке БД,
многоуровневым технологиям (COM, CORBA, .Net, J2EE) и
CASE-средствам вы можете найти на сайте www.optim.su
и на страницах журнала.