Технология Клиент-Сервер 2001'1 |
|||||||
|
Владислав Чистяков
Часто при использовании 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-элемента придется создать пару строк вручную.
class ATL_NO_VTABLE CCntrDispEvents : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CCntrDispEvents>, public IDispEventImpl<0, CCntrDispEvents, &DIID__IascContainerCtrlEvents, &LIBID_ASCCONTAINERLib, 1, 0> { …
BEGIN_COM_MAP(CCntrDispEvents) COM_INTERFACE_ENTRY_IID(DIID__IascContainerCtrlEvents, CCntrDispEvents) END_COM_MAP()
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 – имя метода обработчика события.
CComObject<CCntrDispEvents>::CreateInstance(&m_pCntrDispEvents); m_pCntrDispEvents->AddRef(); // 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
Если возникнут проблемы, пишите на , а если хочется разобраться как подключиться к событиям COM-объекта на низком уровне, прочтите Волшебство ActiveX : Пример ActiveX Control и DCOM, использующих ATL.
Copyright © 1994-2016 ООО "К-Пресс"