随笔-60  评论-262  文章-1  trackbacks-0
当年俺为了实现一个连接点接收器, 死了俺太多脑细胞. 今天重新看这一部分内容, 实现了一个模板, 使用相当简单, 它使用 ATL 的实现.

以下是这个代码的头文件 "sinkimpl.h"

#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<*>(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;
}

#endif // !defined( __sinkimpl_h_INCLUDED__ )

将上述内容保存为 "sinkimpl.h" 头文件. 然后将这个头文件包含在 stdafx.h 靠下一点的地方. 然后就可以使用这个类模板 CSinkImpT 了.
来来来, 咱定义一个例子, 用 oleview.exe 查看我们要实现的针对某一事件的类定义:

可以看到, 事件接口 _GoRiDiEvents 有一个事件 Dead 序号是 1, 据此我们写下如下实现代码.
#pragma once

// 1. define my sink class' CLSID, it's must!!! 
class DECLSPEC_UUID("492194D9-7BEE-422D-AE7C-C43A809F20EC") CSink3;

// 2. define my sink class, it is derived from class template CSinkImpT
class ATL_NO_VTABLE CSink3
    : 
public CSinkImpT<CSink3, _IGoRiDiEvents/*, &__uuidof(__LIANJIEDIANLib)*/>
{
public:
    CSink3(
void){ m_pMain=NULL; }
    
virtual ~CSink3(void){}

    typedef CSinkImpT
<CSink3, _IGoRiDiEvents/*, &__uuidof(__LIANJIEDIANLib)*/> _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
            
if( m_pMain ){
                ::MessageBoxW(m_pMain, pdispparams
->rgvarg[0].bstrVal, L"Sink Message", MB_OK);
            }
            
return S_OK;
        
default:
            
break;
        }
        
return _parentClass::DoInvoke(dispidMember, riid,
            lcid, wFlags, pdispparams, pvarResult,
            pexcepinfo, puArgErr);
    }

    HWND m_pMain;
    
void SetOwner(HWND pDlg) {
        m_pMain 
= pDlg;
    }
};

将以上内容保存为 sink3.h 文件. 就可以在你需要的地方使用这个接收器了. 接收器的使用相当简便:
{
    HRESULT hr 
= E_FAIL;
    
do 
    {
        
if (m_spGoridi==NULL || m_dwCookie!=0) { break; }

        CComQIPtr
<IConnectionPoint> spCP;
        hr 
= GetConnectPoint(m_spGoridi, __uuidof(_IGoRiDiEvents), &spCP);
        
if (FAILED(hr)){ break; }

        CComQIPtr
<IDispatch> spSink;
        {
            CComObject
<CSink3> * pTmp = NULL;
            hr 
= CComObject<CSink3>::CreateInstance(&pTmp);
            
if (FAILED(hr)){ break; }

            pTmp
->SetOwner(m_hWnd);

            pTmp
->AddRef();
            hr 
= pTmp->QueryInterface(&spSink);
            pTmp
->Release();

            
if (FAILED(hr)){ break; }
        }

        spCP
->Advise(spSink, &m_dwCookie); 

    } 
while (FALSE);
}

至此, 可以接收事件了, 注意, 记下 m_dwCookie 值. 到某个时候, 我们不想接收事件了, 可以像这样取消掉它:
{
    HRESULT hr 
= E_FAIL;
    
do {
        
if (m_dwCookie==0) { break; }

        CComQIPtr
<IConnectionPoint> spCP;

        hr 
= GetConnectPoint(m_spGoridi, __uuidof(_IGoRiDiEvents), &spCP); 
        
if (FAILED(hr)){ break; }

        hr 
= spCP->Unadvise(m_dwCookie);
        m_dwCookie 
= 0
    } 
while (FALSE);
}

上面的两段代码用到了变量 m_spGoridi, 其定义和实例化为:

CComPtr<LIANJIEDIANLib::IGoRiDi> m_spGoridi;
m_spGoridi.CoCreateInstance(__uuidof(LIANJIEDIANLib::GoRiDi));
 

 

 

以上实现的例子代码在此:
http://www.cppblog.com/Files/free2000fly/atlsink.zip

关于实现能响应事件的组件的文章, 网上已经很多了, 这里就是一篇:
http://blog.vckbase.com/teacheryang/archive/2005/09/21/12224.html


PS, 敲完才发现, 以上文章的代码要作点小修改:
template<typename T, typename EventInterface, const GUID & evtLibID >
要改成
template<typename T, typename EventInterface, const GUID * evtLibID=NULL >
才能在 VC6 下编译通过, 因为 vc6 的C++ 实现不支持模板参数是引用, 其它地方也必须做相应修正. 供下载用的 zip 文件里已经修正了.
posted on 2009-05-28 18:21 free2000fly 阅读(4237) 评论(1)  编辑 收藏 引用

评论:
# re: 一个标准的 COM 连接点接收器(Sink)的实现, 使用相当简单!!! 2009-07-20 11:47 | free2000fly
引入类型库的语句

#import "libid:f9152aec-3462-4632-8087-eee3c3cdda35" \
raw_interfaces_only \
no_smart_pointers \
raw_dispinterfaces \
raw_native_types \
no_implementation
  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理