当前位置导航:炫浪网>>网络学院>>编程开发>>C++教程>>Visual C++教程

COM对象与连接点机制及其MFC程序实现

  本文首先论述可连接对象和连接点机制的原理,然后通过一个示例说明怎样用MFC编程实现可连接对象和内嵌于客户的事件接收器.

  1、可连接对象和连接点机制的基本原理

  为了在组件对象和客户之间提供更大的交互能力,组件对象也需要主动与客户进行通信。组件对象通过出接口(Outgoing Interface)与客户进行通信。如果一个组件对象定义了一个或者多个出接口则此组件对象叫做可连接点对象。

  所谓出接口也是COM接口。每个出接口包含一组成员函数,每个成员函数代表了一个事件、一个通知或者一个请求。但是这些接口是在客户的事件接收器(sink)中实现的,所以叫出接口。事件接收器也是COM对象。

  可连接对象必须实现一个IConnectionPointContainer接口用于管理所有的出接口。每个出接口对应一个连接点对象,连接点对象实现了IConnectionPoint接口。客户正是通过IConnectionPoint接口与可连接对象建立连接。每一个连接用CONNECTDATA结构描述。

  CONNECTDATA包含两个成员:IUnknown* pUnk和DWORD dwCookie。pUnk对应于客户中事件接收器的IUnknown接口指针;dwCookie是由连接点对象生成的用于唯一标识此连接的32位整数。

  通过一个由可连接对象实现的枚举器接口IEnumConnectionPoints,客户可以访问可连接对象的所有连接点。但是要获得IEnumConnectionPoints接口指针,要通过IConnectionPointContainer::EnumConnectionPoints(IEnumConnectionPoints**)函数,此函数返回枚举器接口指针。

  通过另一个有可连接对象实现的枚举器接口IEnumConnections,无论客户还是可连接对象都可以访问一个连接点上的所有连接。通过IConnectionPoint::EnumConnections(IEnumConnections**)函数可以获得IEnumConnections接口指针。

  综上所述,一个可连接对象必须实现四个接口:IConnectionPointContainer、IConnectionPoint、IEnumConnectionPoints、IEnumConnections。这四个接口的定义请阅读MSDN文档。

  现在结合后面的示例简单描述一下可连接对象和客户通信的过程。在后面的示例中,可连接对象ConnObject定义了出接口IEventSink,对应此出接口,实现了一个连接点对象SampleConnPoint(此对象实现了对应于出接口的连接点接口IConnectionPoint,接口ID为IID_IEventSink)。

  1.客户在获取了可连接对象的IUnknown接口指针m_pIUnknown后,调用m_pIUnknown->QueryInterface(IID_IConnectionPointContainer,(void**)&pConnPtCont);如果调用成功,pConnPtCont中将存放可连接对象的IConnectionPointContainer接口指针。如果调用不成功,则表明对象不是可连接对象。

  2.调用pConnPtCont->FindConnectionPoint(IID_IEventSink,&pConnPt)。如果调用成功,pConnPt将存放对应于出接口IEventSink的连接点对象SampleConnPoint所实现的连接点接口IConnectionPoint指针;如果调用不成功,说明可连接对象不支持出接口IEventSink。

  3.调用pConnPt->Advise(pIEventSink,&m_dwCookie)以建立事件接收器(EventSink)与连接点的连接。其中pIEventSink是客户事件接收器IUnknown接口的指针,此指针通过此函数传递给了可连接对象以便可连接对象发起对客户的通信;m_dwCookie是连接标识,此值由可连接对象设置由客户保存,客户还要使用此值以断开连接。

  4.可连接对象可以通过连接点调用客户事件接收器中的方法。在客户与连接点成功建立连接后,连接点中已经保存了客户事件接收器接口的指针并可以调用pConnPt->GetConnections()来获取。

  5.客户调用pConnPt->Unadvise(m_dwCookie)来取消连接,同时调用pConnPt->Release()释放连接点对象。
2、编程实例

  现在用MFC实现一个可连接对象,然后写一个极为简单的客户和时间接收器。

  需要说明的是,MFC通过CCmdTarget类实现了IConnectionPointContainer和IEnumConnectionPoints接口,此外,通过CConnectionPoint类实现了IConnectionPoint接口

  1.可连接对象ConnObject

  在这个对象中,实现一个一般的COM接口IEventServer,客户可以使用此接口的方法DoSomething()作一些事情,但主要的是对象将在此处触发事件。SampleConnPoint实现连接点对象。

    (1)在GUIDs.h中写入:

// {EE888B01-EA9C-11d3-97B5-5254AB191930}

static const IID CLSID_ConnObject = //组件ID

{ 0xee888b01, 0xea9c, 0x11d3, { 0x97, 0xb5, 0x52, 0x54, 0xab, 0x19, 0x19, 0x30 } };

// {EE888B02-EA9C-11d3-97B5-5254AB191930}

static const IID IID_IEventServer = //一般的COM接口,客户使用此接口的方法

//DoSomething()

{ 0xee888b02, 0xea9c, 0x11d3, { 0x97, 0xb5, 0x52, 0x54, 0xab, 0x19, 0x19, 0x30 } };


//// {EE888B03-EA9C-11d3-97B5-5254AB191930}

static const IID IID_IEventSink = //连接点对象所实现的连接点接口ID

{ 0xee888b03, 0xea9c, 0x11d3, { 0x97, 0xb5, 0x52, 0x54, 0xab, 0x19, 0x19, 0x30 } };


  2. 在IConnObject.h中写入

#include "GUIDs.h"

//声明IEventServer接口

DECLARE_INTERFACE_(IEventServer,IUnknown)

{

STDMETHOD(DoSomething)()PURE;

};

//声明出接口,此出接口将由客户的事件接收器实现

DECLARE_INTERFACE_(IEventSink,IUnknown)

{

STDMETHOD(EventHandle)()PURE;

};

  3.添加基类为CCmdTarget的类CConnObject.在类声明文件CConnObject1.h中加上#include “IConnObject.h”,在类声明中写入:

protected:

……

//声明实现IEventServer接口的嵌套类

BEGIN_INTERFACE_PART(EventServer,IEventServer)

STDMETHOD(DoSomething)();

END_INTERFACE_PART(EventServer)

DECLARE_INTERFACE_MAP()

//声明实现连接点的嵌套类

BEGIN_CONNECTION_PART(CConnObject,SampleConnPoint)

CONNECTION_IID(IID_IEventSink)

END_CONNECTION_PART(SampleConnPoint)

DECLARE_CONNECTION_MAP()

DECLARE_OLECREATE(CConnObject)

  说明:BEGIN_CONNECTION_PART和END_CONNECTION_PART宏声明了实现连接点的嵌套类SampleConnPoint,并且是基于CConnectionPoint类的,如果需要重载CConnectionPoint类的成员函数或者添加自己的成员函数,可以在这两个宏中声明.这里,CONNECTION_IID宏重载了CConnectionPoint::GetIID()函数.使用DECLARE_CONNECTION-MAP()宏声明连接点映射表.

  4.在类CConnObject的实现文件中写入

IMPLEMENT_OLECREATE(CConnObject,"ConnObject",

0xee888b01, 0xea9c, 0x11d3, 0x97, 0xb5, 0x52, 0x54, 0xab, 0x19, 0x19, 0x30);

BEGIN_INTERFACE_MAP(CConnObject,CCmdTarget)

INTERFACE_PART(CConnObject,IID_IEventServer,EventServer)

INTERFACE_PART(CConnObject,IID_IConnectionPointContainer,ConnPtContainer)

END_INTERFACE_MAP()

BEGIN_CONNECTION_MAP(CConnObject,CCmdTarget)

CONNECTION_PART(CConnObject,IID_IEventSink,SampleConnPoint)

END_CONNECTION_MAP()

说明:A.必须在接口映射中写入INTERFACE_PART(CConnObject,IID_IConnectionPointContainer,ConnPtContainer)以实现IConnectionPointContainer接口.注意,CCmdTarget类内嵌有才ConnPtContainer类以实现IConnectionPointContainer接口,并用m_xConnPtContainer加以记录.

B.用BEGIN_CONNECTION_MAP和END_CONNECTION_MAP宏实现连接点映射.CONNECTION_PART定义了实现连接点的类.

  5.在CConnObject::CConnObject()中写入:

EnableConnections();

  6.实现IEventServer接口

  IEventServer接口是基于IUnknown接口的,实现IUnknown接口的方法这里不在赘述.在实现文件中写入:

STDMETHODIMP

CConnObject::XEventServer::DoSomething()

{

//DoSomething

METHOD_PROLOGUE(CConnObject,EventServer)

pThis->FireEvent();

return S_OK;

}

DoSomething()方法可以为客户提供需要的服务.这里着重的是可连接对象在此处触发客户事件接收器的事件,FireEvent()函数是ConnObject类实现的专门触发事件的的函数,代码如下:

void CConnObject::FireEvent()

{

//获取连接点上的连接指针队列

const CPtrArray* pConnections = m_xSampleConnPoint.GetConnections();

ASSERT(pConnections!=NULL);

int cConnections = pConnections->GetSize();

IEventSink* pIEventSink;

//对每一个连接触发事件

for(int i = 0; i < cConnections; i++)

{

//获取客户事件接收器接口指针

pIEventSink = (IEventSink*)(pConnections->GetAt(i));

ASSERT(pIEventSink!=NULL);

//调用客户事件接受器事件处理函数

//此函数是出接口定义,由客户事件接收器实现的

pIEventSink->EventHandle();

}

}

共2页 首页 上一页 1 2 下一页 尾页 跳转到
相关内容
赞助商链接