本文的更新:借助模板类自动实现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 );
}
};