借助模板类自动实现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
class ATL_NO_VTABLE CSinkImpT
	: public CComObjectRootEx
	, public CComCoClass, &__uuidof(T)>
	, public IDispatchImpl < EventInterface, &__uuidof(EventInterface), evtLibID >
{
public:
	CSinkImpT() {}
	virtual ~CSinkImpT() {}

	typedef IDispatchImpl _parentClass;
	typedef CSinkImpT _thisClass;

	STDMETHOD( Invoke )(DISPID dispidMember, REFIID riid,
						 LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
						 EXCEPINFO* pexcepinfo, UINT* puArgErr)
	{
		T * pThis = static_cast(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 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 cph( pCom );
//
template
class ConnectionPointHelper
{
	CComPtr 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 spCP;
			hr = GetConnectPoint( m_spInterface, __uuidof(EventInterface), &spCP );
			if ( FAILED( hr ) ){ break; }

			CComQIPtr spSink;
			{
				CComObject * pTmp = NULL;
				hr = CComObject::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 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 pUnknown;
		HRESULT hr = pCom.CreateInstance( L"Test.Com" );
		if (SUCCEEDED(hr))
		{
			hr = pCom->QueryInterface( IID_IUnknown, reinterpret_cast(&pUnknown) );
			if ( SUCCEEDED( hr ) )
			{
				ConnectionPointHelper 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 _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 );
	}
};