接收器对象实现某个接口,源对象拥有该接口的指针,源对象可以调用该接口的方法。从而形成源对象以事件的方式通知接受器对象的效果。一个连接包含两部分,源对象和接收器对象。如图:
ISpeaker接口和_ISpeakerEvents接口对于各自的实现对象Demagogue和EarPolitic对象来说,只是个普通的接口。但是,由于Demagogue拥有了EarPolitic对象实现的_ISpeakerEvents接口的指针,使得_ISpeakerEvents变得特殊起来,Demagogue可以调用它的方法来通知EarPolitic对象,形成了“事件”。
连接由EarPolitic对象建立,连接也由EarPolitic对象断开。
如果EarPolitic对象要建立连接,它需要通知Demagogue对象它已经实现了_ISpeakerEvents接口。如果要断开连接,它也需要通知Demagogue对象。如何通知呢:Demaogogue对象应该针对_ISpeakerEvents接口实现一个对应接口IConForSpeakerEvents,该接口要提供Advise和UnAdvise两个方法,这样EarPolitic对象通过调用这两个方法来通知Demagogue对象建立连接或者断开连接。
IConForSpeakerEvents接口存在的目的只是为了让EarPolitic对象获得建立、断开对_ISpeakerEvents接口的方法。因此可以把连接事件接口的动作抽象出来,交由IConnectionPoint(连接点)来管理。
连接点允许接收器对象将事件接口的指针传送给源对象。接收器对象通过调用源对象的IConnectionPoint接口的方法Advise来建立连接。IConnectionPoint接口的方法Unadvise用来断开连接。(也可以运用AtlAdvise和AtlUnadvise函数来简化工作)。
IConnectionPoint : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetConnectionInterface(
/* [out] */ IID *pIID) = 0;
virtual HRESULT STDMETHODCALLTYPE GetConnectionPointContainer(
/* [out] */ IConnectionPointContainer **ppCPC) = 0;
virtual HRESULT STDMETHODCALLTYPE Advise(
/* [in] */ IUnknown *pUnkSink,
/* [out] */ DWORD *pdwCookie) = 0;
virtual HRESULT STDMETHODCALLTYPE Unadvise(
/* [in] */ DWORD dwCookie) = 0;
virtual HRESULT STDMETHODCALLTYPE EnumConnections(
/* [out] */ IEnumConnections **ppEnum) = 0;
};
从Advise方法我们可以看出EarPolitic对象的指针交给了连接点对象后,Demagogue对象会在合适的时候调用该接口的方法,形成“事件”。
一个连接点对象对应一个事件接口,所以如果有不止一个事件接口,就应该有不止一个的连接点对象为其提供连接服务。
IConnectionPointContainer作为包容器,可以包容若干个IConnectionPoint对象。这就实现了同时支持多个连接点。
ATL中,IConnectionPointImpl类是用来实现IConnectionPoint对象的。ATL中,连接点映射表用来存放连接点的必要信息,该表中的每个表项存放连接点接口的GUID。比如:
IConnectionPointContainer接口负责管理多个IConnectionPoint接口。请看定义:
IConnectionPointContainer : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE EnumConnectionPoints(
/* [out] */ IEnumConnectionPoints **ppEnum) = 0;
virtual HRESULT STDMETHODCALLTYPE FindConnectionPoint(
/* [in] */ REFIID riid,
/* [out] */ IConnectionPoint **ppCP) = 0;
};
FindConnectionPoint方法可以很方便的获得容器内部管理的连接对象指针,而IEnumConnectionPoints接口是为了方便枚举容器内部的每个连接对象接口的。
IEnumConnectionPoints : public IUnknown
{
public:
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Next(
/* [in] */ ULONG cConnections,
/* [length_is][size_is][out] */ LPCONNECTIONPOINT *ppCP, // typedef IConnectionPoint* LPCONNECTIONPOINT;
/* [out] */ ULONG *pcFetched) = 0;
virtual HRESULT STDMETHODCALLTYPE Skip(
/* [in] */ ULONG cConnections) = 0;
virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Clone(
/* [out] */ IEnumConnectionPoints **ppEnum) = 0;
};
连接点容器管理了不止一个的连接点,那么这些连接点的信息是如何存储的呢?ATL中使用了连接点映射表来存放连接点管理的事件接口的IID.下面的宏
BEGIN_CONNECTION_POINT_MAP(CDemagogue)
CONNECTION_POINT_ENTRY(DIID__ISpeakerEvents)
END_CONNECTION_POINT_MAP()
用来将DIID__ISpeakerEvents存放到连接点映射表中。
我们的组件类必须保存事件接口指针,如何保存呢,我们的类从CProxy_IEvent1Events<CMyClass>中派生。CProxy_IEvent1Events类又从IConnectionPointImpl类派生。IConnectionPointImpl类里面有一个成员变量m_vec,这通常是一个CComDynamicUnkArray类的变量,CComDynamicUnkArray内部维护了IUnknown**数组
当EarPolitic对象调用IConnectionPointImpl::Advise方法建立连接时,实际上该事件接口指针被加入到m_vec变量中。因此当我们需要激发事件时,实际上就是找到m_vec中保存的对应的接口指针,并调用相应的方法。
CProxy_IEvent1Events将提供很多名称类似于Fire_OnMethod的方法,这些辅助方法将帮助我们发出事件。
注意由于COM+采用的事件方式与COM不同,所以,不要选择添加COM+1.0组件,而应该选择添加ATL简单对象。
支持连接点
向导生成后我们检查代码:
idl文件里面
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(C74F7F62-D315-4BF6-9422-9B80D68DB4FA),
dual,
nonextensible,
helpstring("ISample 接口"),
pointer_default(unique)
]
interface ISample : IDispatch{
};
[
uuid(48D59498-7F95-4C2B-B3C3-B5DA3B407025),
version(1.0),
helpstring("EventSource 1.0 类型库")
]
library EventSourceLib
{
importlib("stdole2.tlb");
[
uuid(87A653F3-F08F-4C1F-B091-5F8FAA894E3C),
helpstring("_ISample事件接口")
]
dispinterface _ISampleEvents
{
properties:
methods:
};
[
uuid(6CC7B493-5F8E-4C08-B66D-D9E5FD2342E0),
helpstring("Sample Class")
]
coclass Sample
{
[default] interface ISample;
[default, source] dispinterface _ISampleEvents;
};
};
红色的就是本组件首选的事件接口。ISampleEvents接口目前没有提供方法和属性。
类的声明如下:
class ATL_NO_VTABLE CSample :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CSample, &CLSID_Sample>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<CSample>,
public CProxy_ISampleEvents<CSample>,
public IDispatchImpl<ISample, &IID_ISample, &LIBID_EventSourceLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
IConnectionPointContainerImpl和CProxy_ISampleEvents<CSample>两个父类是关键。
BEGIN_COM_MAP(CSample)
COM_INTERFACE_ENTRY(ISample)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
END_COM_MAP()
COM MAP宏表明了对象支持IConnectionPointContainer接口
BEGIN_CONNECTION_POINT_MAP(CSample)
CONNECTION_POINT_ENTRY(__uuidof(_ISampleEvents))
END_CONNECTION_POINT_MAP()
上面这个宏将_ISampleEvents接口的IID保存到连接点映射表中。
我们现在想要在_ISampleEvents接口上添加方法OnEvent1(BSTR Msg);当该事件被激发时,通过参数Msg将传递给接收对象一个BSTR。在未来的设计中,接收对象将显示这个Msg。我们将通过向导帮我们实现这个事件方法。
右键点击_IsampleEvents接口,选择“添加方法“。填写下面的对话框。
完成后idl文件将作如下增加:
dispinterface _ISampleEvents
{
properties:
methods:
[id(1), helpstring("方法OnEvent1")] HRESULT OnEvent1([in] BSTR Msg);
};
我们手工的在CProxy_ISampleEvents类添加共有成员函数,该函数辅助激发事件
HRESULT Fire_OnEvent1(BSTR Msg)
{
CComVariant varResult;
T* pT=static_cast<T*>(this);
int nConIndex;
CComVariant* pvars=new CComVariant[1];
int nConnections=m_vec.GetSize();
for(nConIndex=0;nConIndex<nConnections;nConIndex++)
{
CComPtr<IUnknown> sp=m_vec.GetAt(nConIndex);
IDispatch* pDispatch=reinterpret_cast<IDispatch*>(sp.p);
if(pDispatch!=NULL)
{
VariantClear(&varResult);
pvars[0].vt=VT_BSTR;
pvars[0].bstrVal=SysAllocString(Msg);
DISPPARAMS disp={pvars,NULL,1,0};
pDispatch->Invoke(0x1,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,&disp,&varResult,NULL,NULL);
}
}
return varResult.scode;
}
通过向导实现Fire_OnEvent1方法也很简单,右键点击CSample,添加连接点,如图:
点击完成后,向导将生成Fire_OnEvent1的实现代码。
最后,我们添加一个ISample接口的方法Start,当该方法被调用时,他将激发_ISampleEvents接口的事件方法OnEvent1。该方法的实现很简单
STDMETHODIMP CSample::Start(void)
{
// TODO: 在此添加实现代码
CComBSTR Msg(L"haha");
Fire_OnEvent1(Msg);
return S_OK;
}
一切好了,我们下面将编写接受器对象。
分享到:
相关推荐
6. **连接点**:ATL提供了连接点支持,使得一个对象可以暴露自己的事件或方法供其他对象调用。 7. **优化的内存管理**:ATL的内存管理机制有助于减少内存泄漏和提高性能。 WDK中的ATL7.1特别适用于驱动程序开发,...
5. **连接点**:ATL提供了对连接点的支持,允许COM组件之间进行事件传递。 6. **字符串处理**:ATL包含了一些高效字符串处理的函数和宏,以减少内存分配和释放的开销。 7. **模板元编程**:ATL使用模板元编程技术...
4. **连接点**:ATL支持连接点(Connection Points)机制,允许对象向其他对象发布事件或接收事件。 5. ** ATL Simple Objects**:提供了一组基础对象,如CComObject、CComCoClass等,可以快速创建简单的COM对象。 6...
4.1.4 建立接收器与连接点的连接 4.1.5 获得出接口的类型信息 4.2 结构化存储 4.2.1 什么叫结构化存储和复合文件 4.2.2 存储对象和IStorage接口 4.2.2.1 IStorage接口 4.2.2.2 获得IStorage指针 4.2.2.3 释放STATSTG...
CruiseYoung提供的带有详细书签的电子书籍目录 ... 该资料是《COM技术内幕——微软组件对象模型》一书的随书源代码 COM技术内幕——微软... 13.5 事件和连接点 290 13.5.1 IEnumXXX 292 13.6 本章小结 293 结束语 294
本文将深入探讨如何从一个MFC/ATL COM客户端模拟COM连接点来访问VB.NET库,这对于那些需要在现有COM系统中集成.NET组件的开发者来说尤其重要。 首先,理解COM连接点的概念至关重要。COM连接点是一种机制,允许COM...
- **ATL对数据库访问的支持**:利用ATL创建高性能的数据库访问组件。 #### 三、数据库开发过程 - **阶段1:调查与分析**:明确业务需求。 - **阶段2:数据建模**:设计数据库表结构。 - **阶段3:功能设计**:确定...
### MFC+Access 数据库编程知识点详解 #### 一、基础知识篇 **1.1 数据库基本原理** - **概述**:数据库是按照一定结构存储和管理数据的仓库。它不仅包括存储数据本身,还包括相应的元数据(描述数据的数据)。在...
- **ATL对数据库访问的支持**:ATL支持创建高性能的数据库访问组件,可以用于开发复杂的应用程序。 #### 三、数据库开发过程 ##### 3.1 阶段1:调查与分析 - **目的**:明确项目的需求,包括业务流程、数据流图、...
在Windows编程环境中,特别是使用Visual Studio(如VC7.0, VC7.1, 或者VC6)进行MFC (Microsoft Foundation Classes) 或ATL (Active Template Library) 开发时,实现这一功能会涉及一些关键的技术点。 1. **MFC中的...
ActiveX托管的WTL类是Windows Template Library (WTL)框架的一个重要组成部分,它扩展了ATL(Active Template Library)的功能,使得在C++中创建和管理ActiveX控件变得更加简便。WTL是一个轻量级的库,主要用于构建...
6. **与ATL项目集成**:对于使用ATL技术构建的COM组件和ActiveX控件,大丽花调试设备浏览器栏能提供更深入的调试支持。 提供的压缩文件`Dahlia-Debug-Device-Browser-Bar.pdf`很可能是该工具的使用手册或详细文档,...
16.2 创建一个ATL多边形工程 16.2.1 优化模块代码 16.2.2 测试控件 16.3 调试ATLCOM控件 16.4 小结 第17章 STL和 MFC编程 17.l 产生一个STL和MFC应用程序 17.1.l 复数 17.1.2 模板语法 17.1.3 基本的...