之前在借助模板類自動實現COM連接點接收器(Sink)中對原作者的代碼進一步封裝,弄清了連接點使用的原理,在看ATL代碼的過程中,發現ATL本身就提供了AtlAdvise/AtlUnadvise這樣的機制來簡化連接點的使用,CComPtrBase中也有Advise這個成員函數,它是對AtlAdvise,進一步封裝,因此,對ConnectionHelper的代碼可以再簡化,簡化後Connect()只有十來行了。原作者寫的GetConnectPoint函數也用不上了。
#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 ); } }; /////////////////////////////////////////////////////////////////////////////////////////////////////// // ComDllLib::ITestComPtr pCom; // HRESULT hr = pCom.CreateInstance( L"Test.Com" ); // ConnectionPointHelper<ComDllLib::ITestCom, 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; } CComObject<EventProcessor> * pTmp = NULL; hr = CComObject<EventProcessor>::CreateInstance( &pTmp ); if ( FAILED( hr ) ){ break; } CComQIPtr<IUnknown, &IID_IUnknown> spSink( pTmp ); hr = m_spInterface.Advise( spSink, __uuidof(EventInterface), &m_dwCookie ); } while ( FALSE ); } void Disconnect() { HRESULT hr = E_FAIL; do { if ( m_dwCookie == 0 ) { break; } AtlUnadvise( m_spInterface, __uuidof(EventInterface), m_dwCookie ); m_dwCookie = 0; } while ( FALSE ); } }; #endif // !defined( __sinkimpl_h_INCLUDED__ )
以上就是全部代碼,減少到不到100行。
使用方法:
{ // .tlh中的接口方式調用 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 ); } } }
{ // IDispatch方式調用 CComPtr<IDispatch> spDisp; HRESULT hr = spDisp.CoCreateInstance( L"Test.Com" ); if ( SUCCEEDED( hr ) ) { CComQIPtr<IUnknown, &IID_IUnknown> spUnknown( spDisp ); ConnectionPointHelper<ComDllLib::_ITestComEvent, CSink3> cph( spUnknown ); _variant_t ret; _variant_t m = 2, n = 3; spDisp.Invoke2( (LPCOLESTR) L"Add", &m, &n, &ret ); } }