之前在借助模板類自動實現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 );
}
}