当年俺为了实现一个连接点接收器, 死了俺太多脑细胞. 今天重新看这一部分内容, 实现了一个模板, 使用相当简单, 它使用 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<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;
}
#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.htmlPS, 敲完才发现, 以上文章的代码要作点小修改:
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 阅读(4242)
评论(1) 编辑 收藏 引用