码迷,mamicode.com
首页 > 其他好文 > 详细

12.ATL中实现可连接对象和接收器

时间:2016-08-08 09:56:16      阅读:221      评论:0      收藏:0      [点我收藏+]

标签:

和之前讲述的IDispatch的实现类似,相比MFC,ATL实现可连接对象更加简单,编写完IDL,使用MIDL编译即可完成大部分工作。

这里我们使用ATL实现一个Speek组件对象,可通过ISpeek接口说话,根据通过可连接点通知客户说话分贝的大小。


1.实现可连接对象

建立一个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;
}

是不是非常简单?


2.实现接收器

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

只需要在CDispEventHandlerBase中定义函数调用约定、返回类型、参数类型,在事件处理函数映射表添加对应的处理函数即可。


3.连接和断开连接

这里我们在主对话框中继承CDispEventHandler类,如下:

class CMainDlg : public CDialogImpl<CMainDlg>,
				 public CDispEventHandler<CMainDlg>


这样我们可以借助IDispEventSimpleImpl简化连接和断开连接操作,如下:

	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

12.ATL中实现可连接对象和接收器

标签:

原文地址:http://blog.csdn.net/wenzhou1219/article/details/52143670

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!