首页 > 代码库 > 借助模板类自动实现COM连接点接收器(Sink)

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

最初的代码源自free2000fly的一个标准的 COM 连接点接收器(Sink)的实现, 使用相当简单!!!,作者封装了不少工作,但调用时的代码还可以再封装一下,最后只要拷贝并修改Sink实现类的Invoke就好了。

 

以下是这个代码的头文件 "sinkimpl.h",比free2000fly的"sinkimpl.h"多了一个模板类ConnectionPointerHelper<>

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#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__ )

  

使用方法:
+ View Code?
1
2
3
4
5
6
7
8
9
10
11
12
13
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写的一样):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 要响应连接点事件,只需要重写此类
//
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 );
    }
};