Продолжается подписка на наши издания! Вы не забыли подписаться?

Подключение к событиям СOM-объекта на С++

Владислав Чистяков

Часто при использовании 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-элемента придется создать пару строк вручную.

  1. Берете любой класс, реализованный как COM-объект. Главное, чтобы он был унаследован от CComObjectRoot и от CComCoClass. Для создания нового класса проще всего использовать ATL-wizard, в котором выбрать «Simple Object».Проще всего вызвать ATL-wizard и выбрать "Simple Object".
  2. Этот класс нужно унаследовать от IDispEventImpl. Например:
  3. class ATL_NO_VTABLE CCntrDispEvents :
       public CComObjectRootEx<CComSingleThreadModel>,
       public CComCoClass<CCntrDispEvents>,
       public IDispEventImpl<0, CCntrDispEvents, &DIID__IascContainerCtrlEvents, &LIBID_ASCCONTAINERLib, 1, 0>
    { …
  4. Добавляете к COM-карте ваш событийный интерфейс:
  5. BEGIN_COM_MAP(CCntrDispEvents)
      COM_INTERFACE_ENTRY_IID(DIID__IascContainerCtrlEvents, CCntrDispEvents)
    END_COM_MAP()
  6. Добавляете Sink-карту:
  7. BEGIN_SINK_MAP(CCntrDispEvents)
      SINK_ENTRY_EX(0, DIID__IascContainerCtrlEvents, DISPID_XXX, SomeEventHandler)
    END_SINK_MAP()

    где:

    0 - задается вместо диалогового идентификатора элемента
    SINK_ENTRY_EX – вхождение, описывающее событие, обработку которого нужно сделать.
    DIID__IascContainerCtrlEvents – IID нужного событийного интерфейса.
    DISPID_XXX – DISPID метода из событийного интерфейса.
    SomeEventHandler – имя метода обработчика события.

  8. Залезаете в "OLE/COM Object Viewer" (меню Tools), находите описание COM-объекта, к событиям которого нужно подключиться. Находите описание событийного интерфейса и копируете в буфер описание нужного метода.
  9. Возвращаетесь в VC и вставляете скопированное описание внутрь описания класса. В общем, создаете метод с правильными параметрами.
  10. Создаете экземпляр класса (если он еще не создан):
    CComObject<CCntrDispEvents>::CreateInstance(&m_pCntrDispEvents);
    m_pCntrDispEvents->AddRef(); 
    // AddRef нужен, так как у объекта, 
    // созданного таким образом, счетчик равен нулю, и он 
    // самоуничтожится при попытке подключения 
  11. Подключаете объект-источник событий:
m_pCntrDispEvents->DispEventAdvise(pObjectWithEvents);
ATLASSERT(!hr);

Вот и все. Не забудьте отключить событийный объект:

if(m_pCntrDispEvents)
{
   HRESULT hr = m_pCntrDispEvents->DispEventUnadvise(pObjectWithEvents);
   ATLASSERT(!hr);
}

Чтобы заполучить GUID'ы и другую информацию о типах, можно воспользоваться  директивой #import:

#import "xxx.ocx" no_namespace, named_guids, no_implementation,
raw_interfaces_only, raw_dispinterfaces, raw_native_types

Если возникнут проблемы, пишите на e-mail, а если хочется разобраться как подключиться к событиям COM-объекта на низком уровне, прочтите Волшебство ActiveX : Пример ActiveX Control и DCOM, использующих ATL.


Copyright © 1994-2016 ООО "К-Пресс"