Как подключиться к событиям С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-элемента придется создать пару строк вручную.
- Берете любой класс, реализованный как 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>
{ ... |
- Добавляете к COM-карте ваш событийный интерфейс:
BEGIN_COM_MAP(CCntrDispEvents)
COM_INTERFACE_ENTRY_IID(DIID__IascContainerCtrlEvents, CCntrDispEvents)
END_COM_MAP() |
- Добавляете Sink-карту: BEGIN_SINK_MAP(CCntrDispEvents)
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 – имя метода обработчика события.
- Залезаете в "OLE/COM Object Viewer" (меню Tools), находите описание COM-объекта, к событиям которого нужно подключиться. Находите описание событийного интерфейса и копируете в буфер описание нужного метода.
- Возвращаетесь в VC и вставляете скопированное описание внутрь описания класса. В общем, создаете метод с правильными параметрами.
- Создаете экземпляр класса (если он еще не создан):
CComObject<CCntrDispEvents>::CreateInstance(&m_pCntrDispEvents);
m_pCntrDispEvents->AddRef();
|
- Подключаете объект-источник событий:
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 |
Если возникнут проблемы, пишите на audit@optim.su, а если хочется разобраться как подключиться к событиям COM-объекта на низком уровне, прочтите Волшебство ActiveX : Пример ActiveX Control и DCOM, использующих ATL.
Впервые статья была опубликована в журнале
<Технология Клиент-Сервер>.
Эту и множество других статей по программированию, разработке БД,
многоуровневым технологиям (COM, CORBA, .Net, J2EE) и
CASE-средствам вы можете найти на сайте www.optim.su
и на страницах журнала.