借助模板类自动实现COM连接点接收器(Sink)更新


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


///////////////////////////////////////////////////////////////////////////////////////////////////////
// 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; }

			CComObject * pTmp = NULL;
			hr = CComObject::CreateInstance( &pTmp );
			if ( FAILED( hr ) ){ break; }

			CComQIPtr 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 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 );
				}
			}
		}
		{ // IDispatch方式调用
			CComPtr spDisp;
			HRESULT hr = spDisp.CoCreateInstance( L"Test.Com" );
			if ( SUCCEEDED( hr ) )
			{
				CComQIPtr spUnknown( spDisp );
				ConnectionPointHelper cph( spUnknown );
				_variant_t ret;
				_variant_t m = 2, n = 3;
				spDisp.Invoke2( (LPCOLESTR) L"Add", &m, &n, &ret );
			}
		}