标签:
和之前讲述的IDispatch的实现类似,相比MFC,ATL实现可连接对象更加简单,编写完IDL,使用MIDL编译即可完成大部分工作。
这里我们使用ATL实现一个Speek组件对象,可通过ISpeek接口说话,根据通过可连接点通知客户说话分贝的大小。
建立一个ATL工程,右键添加类->选择ATL简单对象,
填写组件命名后,如下:
然后选择为当前接口实现连接点,如下:
勾选连接点,然后我们编写IDL如下,具体可参考ATL实现COM一文。
import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(554A2800-7862-43EC-9DE6-799CB11AF71B), dual, nonextensible, helpstring("ISpeek 接口"), pointer_default(unique) ] interface ISpeek : IDispatch{ [id(1), helpstring("方法Speek")] HRESULT Speek([in] LONG nVolume); }; [ uuid(163A45FC-1B75-4328-85CA-CFFDBD7A3F5C), version(1.0), helpstring("AtlConnectPoint 1.0 类型库") ] library AtlConnectPointLib { importlib("stdole2.tlb"); [ uuid(E44310A3-B5FB-4F97-AFB2-A69287993C23), helpstring("_ISpeekEvents 接口") ] dispinterface _ISpeekEvents { properties: methods: [id(1), helpstring("方法OnWhispter")] void OnWhistper([in] LONG nVolume); [id(2), helpstring("方法OnTalk")] void OnTalk([in] LONG nVolume); }; [ uuid(A7588351-C619-41FA-AEB8-8C92A814B9EA), helpstring("Speek Class") ] coclass Speek { [default] interface ISpeek; [default, source] dispinterface _ISpeekEvents; }; };可以看到,
这里生成的组件对象是Speek,注意此时前面带有标识[default,source],标识默认的源接口
ISpeek接口由于我们勾选的是双重接口,所以从IDisptach派生,
生成的可连接点是_ISpeekEvents,这里我们指定对应的方法OnWhisper和OnTalk的id分别为1和2,注意类型是dispinterface
点击编译,
此时生成的组件对象定义如下:
class ATL_NO_VTABLE CSpeek : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CSpeek, &CLSID_Speek>, public IConnectionPointContainerImpl<CSpeek>, public CProxy_ISpeekEvents<CSpeek>, public IDispatchImpl<ISpeek, &IID_ISpeek, &LIBID_AtlConnectPointLib, /*wMajor =*/ 1, /*wMinor =*/ 0>其中
IConnectionPointContainerImpl实现了连接点容器
IDispatchImpl实现了接口ISpeek
CProxy_ISpeekEvents实现了对应的连接点
查看接口映射表和连接点映射表如下:
BEGIN_COM_MAP(CSpeek) COM_INTERFACE_ENTRY(ISpeek) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IConnectionPointContainer) END_COM_MAP() BEGIN_CONNECTION_POINT_MAP(CSpeek) CONNECTION_POINT_ENTRY(__uuidof(_ISpeekEvents)) END_CONNECTION_POINT_MAP()分别将接口和连接点加入对应的容器中管理。
我们在自动生成的_ISpeekEvents_CP.h文件中查看CProxy_ISpeekEvents实现如下:
#pragma once template<class T> class CProxy_ISpeekEvents : public IConnectionPointImpl<T, &__uuidof(_ISpeekEvents)> { public: HRESULT Fire_OnTalk( LONG nVolume) { HRESULT hr = S_OK; T * pThis = static_cast<T *>(this); int cConnections = m_vec.GetSize(); for (int iConnection = 0; iConnection < cConnections; iConnection++) { pThis->Lock(); CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection); pThis->Unlock(); IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p); if (pConnection) { CComVariant avarParams[1]; avarParams[0] = nVolume; avarParams[0].vt = VT_I4; DISPPARAMS params = { avarParams, NULL, 1, 0 }; hr = pConnection->Invoke(2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); } } return hr; } HRESULT Fire_OnWhistper( LONG nVolume) { HRESULT hr = S_OK; T * pThis = static_cast<T *>(this); int cConnections = m_vec.GetSize(); for (int iConnection = 0; iConnection < cConnections; iConnection++) { pThis->Lock(); CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection); pThis->Unlock(); IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p); if (pConnection) { CComVariant avarParams[1]; avarParams[0] = nVolume; avarParams[0].vt = VT_I4; DISPPARAMS params = { avarParams, NULL, 1, 0 }; hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); } } return hr; } };可以看到,实际具体连接点实现是IConnectionPointImpl,
这里自动生成Fire_OnTalk和Fire_OnWhistper分别通知客户执行对应操作。
我们需要做的只有实现ISpeek接口,根据传入的说话声大小回调通知客户操作,如下:
STDMETHODIMP CSpeek::Speek( LONG nVolume ) { if (nVolume <= 200) //小声说话 { Fire_OnWhistper(nVolume); } else //大声说话 { Fire_OnTalk(nVolume); } return S_OK; }
ATL中接收器的实现一般采用IDispEventSimpleImpl来实现,这个其实就是IDisptach的另一种查表实现,其原理和MFC中的DISPATCH_MAP非常类似,只不过这里叫做SINK_MAP。
这里我将其做成一个通用的模板类,可以直接套用,如下:
//定义事件源,可以自己指定,不同的事件源保持不同即可。多个事件源时,以此区分 #define SOURCE_ID_SPEEK 1 class CDispEventHandlerBase { public: //定义不同事件源的不同DISP ID的TypeInfo static _ATL_FUNC_INFO OnWhisperInfo; static _ATL_FUNC_INFO OnTalkInfo; }; template <class T> class CDispEventHandler:public CDispEventHandlerBase, public IDispEventSimpleImpl<SOURCE_ID_SPEEK, T, &__uuidof(_ISpeekEvents)> { public: //定义不同事件源的不同DISP ID static const int SPEEK_DISP_ID_WHISPER = 1; static const int SPEEK_DISP_ID_TALK = 2; //定义不同事件源的不同DISP ID的处理函数,子类中必须实现对应的函数 void STDMETHODCALLTYPE OnWhisper(LONG nVolume) { T* pT = static_cast<T*>(this); return pT->OnWhisper(nVolume); } void STDMETHODCALLTYPE OnTalk(LONG nVolume) { T* pT = static_cast<T*>(this); return pT->OnTalk(nVolume); } //定义事件处理函数映射表 BEGIN_SINK_MAP(T) SINK_ENTRY_INFO(SOURCE_ID_SPEEK, __uuidof(_ISpeekEvents), SPEEK_DISP_ID_WHISPER, OnWhisper, &OnWhisperInfo) SINK_ENTRY_INFO(SOURCE_ID_SPEEK, __uuidof(_ISpeekEvents), SPEEK_DISP_ID_TALK, OnTalk, &OnTalkInfo) END_SINK_MAP() }; __declspec(selectany) _ATL_FUNC_INFO CDispEventHandlerBase::OnWhisperInfo = {CC_STDCALL, VT_EMPTY, 1, {VT_I4}}; __declspec(selectany) _ATL_FUNC_INFO CDispEventHandlerBase::OnTalkInfo = {CC_STDCALL, VT_EMPTY, 1, {VT_I4}};
这里我们在主对话框中继承CDispEventHandler类,如下:
class CMainDlg : public CDialogImpl<CMainDlg>, public CDispEventHandler<CMainDlg>
BOOL ConnectSource() { HRESULT hr; hr = CoCreateInstance(__uuidof(Speek), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISpeek), (LPVOID*)&m_pSpeek); if(FAILED(hr) && !m_pSpeek) { return FALSE; } hr = CDispEventHandler::DispEventAdvise(m_pSpeek, &__uuidof(_ISpeekEvents)); if (FAILED(hr)) { return FALSE; } return TRUE; } BOOL DisConnectSource() { HRESULT hr = E_FAIL; hr = CDispEventHandler::DispEventUnadvise(m_pSpeek, &__uuidof(_ISpeekEvents)); if (FAILED(hr)) { return FALSE; } if (m_pSpeek && SUCCEEDED(m_pSpeek->Release())) { m_pSpeek = NULL; return TRUE; } return FALSE; }连接和断开连接的时候,只需要借助一个源对象的接口和待连接的接口IID即可。
那么,我么剩下我们需要做的就很简单了,实现源对象通知时,客户需要做的事情,这里弹框提示,如下:
/****************************接收器实现**********************************/ void STDMETHODCALLTYPE OnWhisper(LONG nVolume) { CString csStrInfo; csStrInfo.Format(L"小声说话,分贝:%d", nVolume); ::MessageBox(NULL, csStrInfo, L"通知", MB_OK); } void STDMETHODCALLTYPE OnTalk(LONG nVolume) { CString csStrInfo; csStrInfo.Format(L"大声吼叫,分贝:%d", nVolume); ::MessageBox(NULL, csStrInfo, L"通知", MB_OK); }
ATL实现可连接对象和连接点方法下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219
标签:
原文地址:http://blog.csdn.net/wenzhou1219/article/details/52143670