В MFC попроще, там просто импортируется ActiveX контрол, у которого есть все события, а для ATL я нашёл только такой способ: Во-первых нужен класс стока событий, ктороый должен реализовывать интерфейс IDispatch. EventSink.h | Код | #pragma once
// Вызывается при вызове метода QueryInterface HRESULT __stdcall CallThisForEveryQI(void* pv, REFIID riid, void** ppv, DWORD dw);
//*------------------------------------------------------------- // Служит для перехвата событий, приходящих от COM-сервера //*------------------------------------------------------------- class CEventSink: public CComObjectRootEx<CComSingleThreadModel>, public IDispatch { public: BEGIN_COM_MAP(CEventSink) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY_FUNC_BLIND(0, CallThisForEveryQI) END_COM_MAP()
STDMETHOD(GetTypeInfoCount)(UINT* pctinfo); STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo); STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid); STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr);
CEventSink(void); ~CEventSink(void); };
|
EventSink.cpp | Код | #include "StdAfx.h" #include "EventSink.h"
CEventSink::CEventSink(void){} CEventSink::~CEventSink(void){}
// // Возвращает указатель на интерфейс // HRESULT __stdcall CallThisForEveryQI(void* pv, REFIID riid, void** ppv, DWORD dw) { // Возвращает вызывающему объекту указатель на интерфейс стока: *ppv = pv; return S_OK; }
//*-------------------------------------- // Вызывается при получании события. // ID Событий содержится в переменной dispidMember. // аргументы находятся в DISPPARAMS. // Если известны имена событий, то их идентификаторы можно получить через функцию COM-сервера GetIDsOfNames //*-------------------------------------- STDMETHODIMP CEventSink::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr) {
TCHAR *flag; switch (wFlags) { case DISPATCH_METHOD: flag=L"wflags= DISPATCH_METHOD"; break; case DISPATCH_PROPERTYGET: flag=L"wflags= DISPATCH_PROPERTYGET"; break; case DISPATCH_PROPERTYPUT: flag =L"wflags= DISPATCH_PROPERTYPUT"; break; case DISPATCH_PROPERTYPUTREF: flag = L"wflags= DISPATCH_PROPERTYPUTREF"; break; }
TCHAR txt[512]; _stprintf_s( txt, 512, TEXT("Event fired. EVENT ID: %d\n%s\nNum of params: %d\n"), dispidMember, flag, pdispparams->cArgs ); OutputDebugString( txt ); return S_OK; }
STDMETHODIMP CEventSink::GetTypeInfoCount(UINT* pctinfo) { return E_NOTIMPL; }
STDMETHODIMP CEventSink::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo) { return E_NOTIMPL; }
STDMETHODIMP CEventSink::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid) { return E_NOTIMPL; }
|
И функции для подписки на событий сервера | Код |
LPCONNECTIONPOINT lpCPT; IDispatch *pDisp; // Pointer to the server object IDispatch *pIDispatchobjevent; // Pointer to the event object DWORD dwCookie; // For Advise / Unadvise LPCONNECTIONPOINT lpCPT; // Pointer to the Connection Point IUnknown *pUnkDispatchSink; // Sink interface IUnknown pointer CComObject<CPollEventSink> *pMySink;// Sink interface pointer
// Подписывается на события сервера void CMyClass::StartMonitoring(IDispatch *obj) { hr = S_OK;
pIDispatchobjevent = (IDispatch*)NULL;// Pointer to the event object lpCPT = NULL; // Pointer to the Connection Point
// Попытаемся подписаться на события
try { LPENUMCONNECTIONPOINTS lpEnumCPTS; unsigned long count;
// Выделяем память под сток событий CComObject<CPollEventSink>::CreateInstance(&pMySink); // Получаем IUnknown-интерфейс стока событий TEST_COM_CALL( pMySink->QueryInterface(IID_IUnknown, (void**)&pUnkDispatchSink) );
// Получаем доступ к интерфейсу, позволяещему пречислить все точки соединения IConnectionPointContainer* pCPContainer;
// Получаем доступ к точкам соединения сервера obj->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPContainer); pCPContainer->EnumConnectionPoints(&lpEnumCPTS); lpEnumCPTS->Reset(); // Получаем точки соединения while(S_OK==(hr = lpEnumCPTS->Next(1, &lpCPT, &count))) { // Связываем сервер и сток hr = lpCPT->Advise(pUnkDispatchSink, &dwCookie); } // Чистим память TEST_COM_CALL( lpEnumCPTS->Release() ); lpEnumCPTS = NULL; } catch( ... ) { } } // Отписывается от событий сервера void CMyClass::StopMonitoring() { // Отписываемся от стока событий lpCPT->Unadvise(dwCookie); lpCPT->Release(); }
|
В двух словах: Создаём реализацию IDispatch, находим точки соединения в COM-сервере, делаем Advise для связи экземпляра стока событий с сервером и радуемся. Кстати, для того, чтобы события от VB компонентов приходили нормально, нужно в потоке стока событий поместить цикл обработки сообщений. Вроде, всё. Если будут вопросы, пиши.
--------------------
Ctrl+Alt+Reset
|