借助模板类自动实现COM连接点接收器(Sink)
本文的更新:借助模板类自动实现COM连接点接收器(Sink)更新 (2014-06-09 17:09)
最初的代码源自free2000fly的一个标准的 COM 连接点接收器(Sink)的实现, 使用相当简单!!!,作者封装了不少工作,但调用时的代码还可以再封装一下,最后只要拷贝并修改Sink实现类的Invoke就好了。
以下是这个代码的头文件 "sinkimpl.h",比free2000fly的"sinkimpl.h"多了一个模板类ConnectionPointerHelper<>
#if !defined( __sinkimpl_h_INCLUDED__ ) #define __sinkimpl_h_INCLUDED__ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 template<typename T, typename EventInterface, const GUID * evtLibID = NULL > class ATL_NO_VTABLE CSinkImpT : public CComObjectRootEx<CComSingleThreadModel> , public CComCoClass<CSinkImpT<T, EventInterface, evtLibID>, &__uuidof(T)> , public IDispatchImpl < EventInterface, &__uuidof(EventInterface), evtLibID > { public: CSinkImpT() {} virtual ~CSinkImpT() {} typedef IDispatchImpl<EventInterface, &__uuidof(EventInterface), evtLibID> _parentClass; typedef CSinkImpT<T, EventInterface, evtLibID> _thisClass; STDMETHOD( Invoke )(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr) { T * pThis = static_cast<T *>(this); return pThis->DoInvoke( dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); } DECLARE_NO_REGISTRY() DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP( _thisClass ) COM_INTERFACE_ENTRY( IDispatch ) COM_INTERFACE_ENTRY( EventInterface ) END_COM_MAP(); STDMETHOD( DoInvoke )(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr) { return _parentClass::Invoke( dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); } }; inline HRESULT WINAPI GetConnectPoint( IUnknown * pItf, const IID & rSinkIID, IConnectionPoint ** ppCP ) { HRESULT hr = E_FAIL; do { if ( pItf == NULL || ppCP == NULL ) { break; } CComQIPtr<IConnectionPointContainer> spContainer; hr = pItf->QueryInterface( &spContainer ); if ( FAILED( hr ) ) { break; } hr = spContainer->FindConnectionPoint( rSinkIID, ppCP ); } while ( FALSE ); return hr; } /////////////////////////////////////////////////////////////////////////////////////////////////////// // 使用方法: // ComDllLib::ITestComPtr pCom; // HRESULT hr = pCom.CreateInstance( L"Test.Com" ); // ConnectionPointHelper<ComDllLib::_ITestComEvent, CSink3> cph( pCom ); // template<typename EventInterface, typename EventProcessor> class ConnectionPointHelper { CComPtr<IUnknown> m_spInterface; DWORD m_dwCookie; public: ConnectionPointHelper( IUnknown* pInterface ) : m_spInterface( pInterface ), m_dwCookie( 0 ) { Connect(); } ~ConnectionPointHelper() { Disconnect(); } protected: void Connect() { HRESULT hr = E_FAIL; do { if ( m_spInterface == NULL || m_dwCookie != 0 ) { break; } CComQIPtr<IConnectionPoint> spCP; hr = GetConnectPoint( m_spInterface, __uuidof(EventInterface), &spCP ); if ( FAILED( hr ) ){ break; } CComQIPtr<IDispatch> spSink; { CComObject<EventProcessor> * pTmp = NULL; hr = CComObject<EventProcessor>::CreateInstance( &pTmp ); if ( FAILED( hr ) ){ break; } pTmp->AddRef(); hr = pTmp->QueryInterface( &spSink ); pTmp->Release(); if ( FAILED( hr ) ){ break; } } spCP->Advise( spSink, &m_dwCookie ); } while ( FALSE ); } void Disconnect() { HRESULT hr = E_FAIL; do { if ( m_dwCookie == 0 ) { break; } CComQIPtr<IConnectionPoint> spCP; hr = GetConnectPoint( m_spInterface, __uuidof(EventInterface), &spCP ); if ( FAILED( hr ) ){ break; } hr = spCP->Unadvise( m_dwCookie ); m_dwCookie = 0; } while ( FALSE ); } }; #endif // !defined( __sinkimpl_h_INCLUDED__ )
使用方法:
UIAddChildWindowContainer( m_hWnd ); ComDllLib::ITestComPtr pCom; CComPtr<IUnknown> pUnknown; HRESULT hr = pCom.CreateInstance( L"Test.Com" ); if (SUCCEEDED(hr)) { hr = pCom->QueryInterface( IID_IUnknown, reinterpret_cast<void**>(&pUnknown) ); if ( SUCCEEDED( hr ) ) { ConnectionPointHelper<ComDllLib::_ITestComEvent, CSink3> cph( pUnknown ); LONG c = pCom->Add( 1, 5 ); } }
CSink3的实现(与free2000fly写的一样):
// 要响应连接点事件,只需要重写此类 // class DECLSPEC_UUID( "492194D9-7BEE-422D-AE7C-C43A809F20EC" ) CSink3; class ATL_NO_VTABLE CSink3 : public CSinkImpT < CSink3, ComDllLib::_ITestComEvent > { public: CSink3( void ) { } virtual ~CSink3( void ) {} typedef CSinkImpT<CSink3, ComDllLib::_ITestComEvent> _parentClass; STDMETHOD( DoInvoke )(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr) { // 3. the dispidMember must referenced from .thl file, and you can have a look using oleview.exe switch ( dispidMember ) { case 1: { CComVariant result( *pvarResult ); if ( SUCCEEDED( result.ChangeType( VT_BSTR ) ) ) ::MessageBoxW( ::GetActiveWindow(), result.bstrVal, L"Sink Message", MB_OK ); return S_OK; } default: break; } return _parentClass::DoInvoke( dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); } };