之前在借助模板类自动实现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 ); } }