十二. 可连接对象 概念与模型 轮询 通知 出接口 连接点机制 IConnectionPointContainer接口

Slides:



Advertisements
Similar presentations
7.1 内置对象概述及分类 JSP 视频教学课程. JSP2.2 目录 1. 内置对象简介 1. 内置对象简介 2. 内置对象分类 2. 内置对象分类 3. 内置对象按功能区分 3. 内置对象按功能区分 4. 内置对象作用范围 4. 内置对象作用范围.
Advertisements

阻塞操作. 在 linux 里,一个等待队列由一个 wait_queue_head_t 类型的结构来描述 等待队列的初始化: static wait_queue_head_t testqueue; init_waitqueue_head(&testqueue);
Oracle数据库 Oracle 子程序.
第14章 c++中的代码重用.
全国计算机等级考试 二级基础知识 第二章 程序设计基础.
在PHP和MYSQL中实现完美的中文显示
潘爱民 COM开发 潘爱民
Using C++ The Weird Way Something about c++11 & OOP tricks
Kvm异步缺页中断 浙江大学计算机体系结构实验室 徐浩.
潘爱民 COM对象的实现(续) 潘爱民
八. COM跨进程特性 进程外组件 列集 标准列集过程 总体结构 存根 代理 接口列集器 ORPC通道 标准列集的实现 自定义列集
程式設計實作.
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
十四. 名字对象 概念 IMoniker接口 名字对象的创建 根据显示名创建名字对象 类名字对象的创建 其他名字对象的创建
辅导课程六.
网络常用常用命令 课件制作人:谢希仁.
五 COM对象 接口及其接口方法的实现 注册表 类厂 COM组件与客户程序的交互过程 类厂的由来 类厂的定义与实现 类厂的创建
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
PaPaPa项目架构 By:Listen 我在这.
泛型委托 泛型接口、方法和委托.
COM:moniker、UDT、control
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
潘爱民 COM:可连接对象 & 结构化存储 潘爱民
第七章 操作符重载 胡昊 南京大学计算机系软件所.
宁波市高校慕课联盟课程 与 进行交互 Linux 系统管理.
宁波市高校慕课联盟课程 与 进行交互 Linux 系统管理.
潘爱民 自动化(Automation) 潘爱民
SOA – Experiment 2: Query Classification Web Service
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
$9 泛型基础.
本节内容 随机读取 视频提供:昆山爱达人信息技术有限公司.
DQMClientDim.cxx及双光子练习
VisComposer 2019/4/17.
实验四、TinyOS执行机制实验 一、实验目的 1、了解tinyos执行机制,实现程序异步处理的方法。
第二章 Java基本语法 讲师:复凡.
VB与Access数据库的连接.
四 COM接口 接口的结构与描述 IUnknown 接口 C++, C, Delphi IDL 接口的的标识 方法与结果 数据类型
分裂对象模型 C++ otcl.
六 . COM接口的其他实现方法 基于表格驱动的接口查询 接口查询的本质 宏 应用 多重继承下的名字冲突 潜在的缺陷 临时的方案
本节内容 Win32 API中的宽字符 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
信号量(Semaphore).
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第二章 Java语法基础.
iSIGHT 基本培训 使用 Excel的栅栏问题
_13简单的GDI绘图操作 本节课讲师——void* 视频提供:昆山爱达人信息技术有限公司 官网地址:
Chapter 18 使用GRASP的对象设计示例.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
Visual Basic程序设计 第13章 访问数据库
郑 昀 应用开发事业部 神州泰岳 SIP多方会话消息 之实例讲解 郑 昀 应用开发事业部 神州泰岳
辅导课程十五.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
Python 环境搭建 基于Anaconda和VSCode.
本节内容 Windows线程切换_时钟中断切换 视频提供:昆山滴水信息技术有限公司 官网地址: 论坛地址: QQ交流 :
WSAAsyncSelect 模型 本节内容 视频提供:昆山爱达人信息技术有限公司 视频录制:yang
_07多连接之select模型 本节课讲师——void* 视频提供:昆山爱达人信息技术有限公司 官网地址:
阻塞式模型 本节内容 视频提供:昆山爱达人信息技术有限公司 视频录制:yang 官网地址:
基于列存储的RDF数据管理 朱敏
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
Chinese Virtual Observatory
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
第2章 Java语言基础.
WEB程序设计技术 数据库操作.
本节内容 如何调试驱动程序? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
使用Fragment 本讲大纲: 1、创建Fragment 2、在Activity中添加Fragment
本节内容 this指针 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
§2 自由代数 定义19.7:设X是集合,G是一个T-代数,为X到G的函数,若对每个T-代数A和X到A的函数,都存在唯一的G到A的同态映射,使得=,则称G(更严格的说是(G,))是生成集X上的自由T-代数。X中的元素称为生成元。 A变, 变 变, 也变 对给定的 和A,是唯一的.
Presentation transcript:

十二. 可连接对象 概念与模型 轮询 通知 出接口 连接点机制 IConnectionPointContainer接口 十二. 可连接对象 概念与模型 轮询 通知 出接口 连接点机制 IConnectionPointContainer接口 IConnectionPoint接口 接收器的实现 连接过程 事件的激发与处理 IDiapatch出接口 MFC对连接的支持 宏 事件激发 例子:用MFC实现源对象 例子:用MFC实现接收器

1 模型与概念 1.1 轮询 假设有一个这样的接口IWaiter. 1 模型与概念 1.1 轮询 假设有一个这样的接口IWaiter. [ object,uuid(2756E11C-A606-482F-969C-14153E1D1601)] interface IWaiter: IUnknown { HRESULT BeginWork(void); //这是一项很费时的任务, 如果是同步执行,客户必须等待它完成. 我们假设它是异步地执行的,客户发出指令后,即刻返回,对象有可能另开辟新的线程进行处理.比如是一个数据库的处理或者是一个科学计算任务.完成以后,客户也得不到任何信息. HRESULT IsOK([out, retval] BOOL *yon); // 刚才吩咐的任务完成了吗? } 在这样的设计模式下,客户的使用方法: IWaiter *pIW; hr=CoCreateInstance(CLSID_Waiter, IID_IWaiter, &pIW); pIW->BiginWork(); //下达命令 BOOL Done=false; while(!Done) { Sleep(10000); //无奈地等待 pIW->IsOK(&Done); //再问一次

1.2 通知 更有效的做法是,由客户提供一个接口,这个接口可以供Waiter使用. (留下一个电话号码,做完了通知我!) [ object,uuid(2756E11C-A606-482F-969C-14153E1D1602)] interface INotify: IUnknown { HRESULT OnWorkIsOk(void);} 我们希望Waiter在完成任务以后能够及时地告知客户,而不是让客户一遍遍的轮询. 现在的问题是客户要有一种方法把这个接口告诉waiter.修改IWaiter接口如下: [ object,uuid(2756E11C-A606-482F-969C-14153E1D1601)] interface IWaiter: IUnknown { HRESULT BeginWork(void); HRESULT Advise([in] INotify* pIN), [out] DWORD *pdwCookie); //客户通过Advise方法提供与此接口任务相关的接口INotify, 而Waiter则返回一个代表这种关联的DWORD值.以后,这个值将被用来解除这种关联使用. HRESULT UnAdvise([in] dwCookie); //客户使用UnAdvise通知Waiter解除与INotify的关联. (嗨,别再烦我了!) }

再来看CWaiter对IWaiter的实现方法, 首先它定义一个成员变量 INotify * m_pIN; HRESULT CWaiter::Advise(INotify* pIN), DWORD *pdwCookie) { if( m_pIN!=NULL) return E_UNEXPECTED; //已经跟别的对象关联上了. m_pIN=pIN; //保存对通知对象的引用,通知对象在客户端. Waiter作为客户端的通知对象的客户! m_pIN->AddRef(); //添加引用计数 *pdwCookie=DWORD(m_pIN); //记录下这种关联 return S_OK; } HRESULT CWaiter::UnAdvise(DWORD dwCookie) { if(DWORD(m_pIN)!=dwCookie) return E_UNEXPECTED // 核对一下 如果不是当前关联的对象. m_pIN->Release(); //减少引用计数 m_pIN=NULL; //再清空

HRESULT CWaiter::BeginWork(void) { assert(m_pIN) //确保已经有了关联 ….// 费时的任务 m_pIN->OnWorkIsOk(); //作完了,通知客户,免得它等得心焦 } 而客户此时的使用则是: INotify * m_pIN= new CNotify; IWaiter *pIW; hr=CoCreateInstance(CLSID_Waiter, IID_IWaiter, &pIW); pIW->Advise(m_pIN, &dwcookie); pIW->BiginWork(); //下达命令 异步的通知: CNotify::OnWorkIsOk() { MessageBox(“ Wake up !, Your work is OK!”); //当pIW完成工作以后通过通告接口调用此函数以通知客户. …… 比前面的轮询的方式好得多.

1.3 出接口 概念: 入接口:客户调用,组件响应。单向通讯 IWaiter 出接口:组件对象主动与客户通讯。组件调用,客户响应。对象通过出接口与客户通讯 INotify 支持出接口的对象称为可连接对象,或源对象。 出接口也是COM接口。包含一组成员函数。每个函数代表一个事件(Event)、通知(Notification)、或请求(Request)。 出接口的含义:在客户方实现,并把接口指针交给对象,对象利用此指针与客户进行通讯。 接收器:实现出接口的对象称为接收器(sink)

客户可以与可连接对象形成一对多或多对一的连接关系 接收器 客户把接收器的 接口指针传给对象 可连接对象调用 接收器的接口成员 客户与可连接对象模型 客户可以与可连接对象形成一对多或多对一的连接关系

2.连接点机制 以上例子中,IWaiter接口的功能意义实际上有了扩展,它不仅能实现BeginWork的功能,而且能够通过INotify接口向其他的个体发出信息. 实现IWaiter接口的可连接对象可以把这种特性公开来,向外界宣称,“如果你想得到完成任务的通知,那么,请实现出接口INotify,并且与我建立连接,把你的电话号码告诉我 . (要收费的哦)” 而出接口则声明“我是对WorkIsOk事件感兴趣的!”. 实际上,在实际的业务逻辑中,可能存在更加复杂的关系,一个源对象有可能还实现了接口IWaitress, 她提供另一种服务,并且使用另一个出接口来完成类似的通告任务. 同时,客户的出接口也有可能与多个可连接对象建立起了连接关系. 为了描述这种多对多的连接关系,COM使用一种更为通用的机制这就是连接点(connection point)机制: 一个可连接对象必须实现IConnectionPointContainer接口,用来管理所有的出接口. 对于每一个出接口,源对象又管理一个连接点对象, 每个连接点对象都实现一个IConnectionPoint接口. 每个连接点对象可以管理多个连接. 如下图:

IConnectionPointContainer 可连接对象 接收器 IConnectionPointContainer 连接点对象 枚举器 IConnectionPoint 一个可连接对象可支持多个出接口,在该对象的IConnectionPointContainer接口中使用一个枚举器暴露它所支持的所有出接口。 对于每一个出接口的连接点对象,在它的IConnetionPoint接口中使用一个枚举器管理它连接的所有的接收器。 枚举器(Enumerator)也是一个COM对象,它可以来访问一组数据单元。它只暴露枚举接口。

2.1 IConnectionPointContainer接口 IConnectionPointContainer : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE EnumConnectionPoints( IEnumConnectionPoints **ppEnum) = 0; virtual HRESULT STDMETHODCALLTYPE FindConnectionPoint( REFIID riid, IConnectionPoint **ppCP) = 0; }; EnumConnectionPoints返回连接点枚举器,客户可以使用此枚举器访问所有的连接点。 FindConnectionPoint根据客户指定的出接口IID,返回相应的连接点。

2.2 IConnectionPoint接口 每个连接点对象对应一个出接口,它只实现IConnetionPoint接口。 IConnectionPoint : public IUnknown { public: virtual HRESULT GetConnectionInterface( IID *pIID) = 0; // 返回对应出接口的IID virtual HRESULTGetConnectionPointContainer( IConnectionPointContainer * *ppCPC) = 0; // 返回源对象IConnectionPointContainer的指针 virtual HRESULT Advise( IUnknown *pUnkSink, DWORD *pdwCookie) = 0; // 被客户用来建立接收器与源对象的连接。 virtual HRESULT Unadvise(DWORD dwCookie) = 0; // 被客户用来取消接收器与源对象的连接。 virtual HRESULT EnumConnections( IEnumConnections **ppEnum) = 0; };// 返回一个连接枚举器接口指针,被客户用来访问所有建立在此连接点对象上的连接。 连接使用CONNECTDATA数据来描述。CONNECTDATA包含两个数据成员,IUnkown类型的pUnk和DWORD类型的dwCookie,pUnk对应于接收器对象的接口指针,而dwCookie是连接点对象生成的一个整数。Unadvise将利用此整数找到相应的连接并取消它。

2.3 接收器的实现 class CSomeEventSet :public ISomeEventSet 2.3 接收器的实现 class CSomeEventSet :public ISomeEventSet { private : int m_Ref; public : DWORD m_dwCookie; // 连接键 CSomeEventSet(); ~CSomeEventSet(); virtual HRESULT _stdcall QueryInterface( REFIID riid, void **ppv) ; virtual ULONG _stdcall AddRef( void) ; virtual ULONG _stdcall Release( void) ; virtual HRESULT SomeEventFunction(......); ......} 接收器完全是客户的内部对象,不需要类厂来创建,也不需要CLSID。当然,它的接口ISomeEventSet是有明确的IID的。 客户可以直接使用new操作符创建接收器。

3 连接过程 3.1 连接过程 ISomeEventSet *pSomeEventSet; IUnkown *pUnk; 3 连接过程 3.1 连接过程 ISomeEventSet *pSomeEventSet; IUnkown *pUnk; IConnectionPointerContainer* pConnectinoPointContainer; IConnectionPoint *pConnectionPoint; //各种接口指针的声明 .... CSomeEventSet *pSink=new CSomeEventSet; //1 创建接收器对象 pSink->QueryInterface(IID_ISomeEventSet,&pSomeEventSet); // 2 返回接收器对象的接口指针 CoCreateInstance(CLSID_ISourceObj,NULL,CLSCTX_INPROC_SERVER,IID_IUnkown,&pUnk); //3 创建源对象,返回IUnkown接口指针 pUnk->QueryInterface(IID_IConnectionPointContainer, &pConnectPointContainer); // 4向源对象查询IConnectionPointContainer接口 pConnectionPointContainer->FindConnectionPoint(IID_ISomeEventSet, &pConnectionPoint); //5向源对象查询支持出接口的连接点对象 pConnectionPoint->Advise(pSomeEventSet,&pSink->m_dwCookie);//6 建立连接 ......// 7在此期间,源对象可以利用pSomeEventSet调用接收器对象的方法。见下 pConnectionPoint->Unadvise(pSink->m_dwCookie);//8断开连接 ......

3.2事件的激发与处理 接收器和源对象连接建立起来后,源对象可以激发事件或向客户发起请求,事件可以由:1.源对象的入接口成员函数激发。2.用户的操作激发。3.其他对象或客户调用激发。 BOOL CSourceObj::FireSomeEvent() { IEnumConnections * pEnum; IConnectionPoint *pConnectionPoint; CONNECTDATA connectionData; ISomeEventSet * pSomeEventSet; FindConnectionPoint(IID_ISomeEventSet,&pConnectionPoint); //寻找与此出接口对应的连接点对象 pConnectionPoint->EnumConnections(&pEnum);//得到连接枚举器 //对此连接点上连接的所有接收器发出请求 while(pEnum->Next(1,&connectionData,NULL)==NOERROR) { if(SUCCEEDED(connectionData.pUnk-> QueryInterface(IID_ISomeEventSet,(void**)&pSomeEventSet))) //由连接数据中取出接收器对象指针,此指针在Advise时由客户传入 //由此接口指针再查询出接口指针。 { pSomeEventSet->SomeEventFunction(); //调用出接口的函数。 pSomeEventSet->Release();} pEnum->Release(); return TRUE;}

4 IDispatch出接口 在编译时刻,客户不一定知道源对象支持什么出接口。客户只知道源对象实现了IID_ISourceObj入接口,然后通过查询IConnectionPointContainer知道它是支持出接口的。当然客户可以调用IConnectionPointerContainer的EnumConnectionPoint函数得到连接点枚举器,以此枚举器逐个得到连接点,然后调用连接点的IConnectionPoint::GetConnectionInterface函数得到它所支持的出接口IID。 然后,客户必须获得出接口的成员函数信息。然后动态地创建接收器对象。然而需要高度的编程技巧。

以IDispatch接口作为出接口。 IDispatch接口把所有的调用都通过Invoke函数来实现,并且提供了管理属性和方法的分发ID机制,以及一套描述参数和返回值的方法,所以使得运行时刻动态绑定属性和方法成为可能。 用IDispatch接口作为出接口可以解决接收器的动态创建过程。利用IDispatch作为源对象的出接口,由源对象提供出接口的类型信息,客户程序根据这些信息,在invoke函数中调用相应的事件控制函数。 过程: 1。从IDispatch接口派生新的接口作为出接口,把方法和属性加到派生接口中,并为之赋予分发ID。源对象通过类型库或IProvideClassInfo接口暴露出接口的类型信息。以后,源对象调用请求时,使用IDispatch::Invoke即可。 2。客户按照源对象提供的出接口类型信息实现接收器对象。接收器只需要实现IUnkown的成员函数和Invoke成员函数即可。由于出接口时源对象定义的,它当然知道接口的每个方法和属性以及其分发ID,所以接收器对象不需要实现其他的几个函数。

5 MFC对连接的支持。 5.1 宏 MFC实现了连接点类 CConnetionPoint 5.1 宏 MFC实现了连接点类 CConnetionPoint CConnectionPoint实现了IConnectionPoint接口。(见P189 ) CCmdTarget提供了一组宏来支持连接点对象。 1。使用DECLARE_CONNECTION_MAP()来定义连接点映射表以及以及有关表的操作函数。 #define DECLARE_CONNECTION_MAP() \ private: \ static const AFX_CONNECTIONMAP_ENTRY _connectionEntries[]; \ protected: \ static AFX_DATA const AFX_CONNECTIONMAP connectionMap; \ static const AFX_CONNECTIONMAP* PASCAL _GetBaseConnectionMap(); \ virtual const AFX_CONNECTIONMAP* GetConnectionMap() const; \

2。使用BEGIN_CONNECTION_MAP, CONNECTION_PART 和END_CONNECTION_MAP()来对连接映射表赋值。 #define BEGIN_CONNECTION_MAP(theClass, theBase) \ const AFX_CONNECTIONMAP* PASCAL theClass::_GetBaseConnectionMap() \ { return &theBase::connectionMap; } \ const AFX_CONNECTIONMAP* theClass::GetConnectionMap() const \ { return &theClass::connectionMap; } \ AFX_COMDAT const AFX_DATADEF AFX_CONNECTIONMAP theClass::connectionMap = \ { &theClass::_GetBaseConnectionMap, &theClass::_connectionEntries[0], }; \ AFX_COMDAT const AFX_DATADEF AFX_CONNECTIONMAP_ENTRY theClass::_connectionEntries[] = \ { \ #define CONNECTION_PART(theClass, iid, localClass) \ { &iid, offsetof(theClass, m_x##localClass) }, \ #define END_CONNECTION_MAP() \ { NULL, (size_t)-1 } \ }; \ 其中也使用了offset宏来计算类的嵌套类成员到父类的偏移。

3。使用BEGIN_CONNECTION_PART ,CONNECTION_IID,和END _CONNECTION_PART 来定义内嵌的嵌套类连接点对象。 #define BEGIN_CONNECTION_PART(theClass, localClass) \ class X##localClass : public CConnectionPoint \ { \ public: \ X##localClass() \ { m_nOffset = offsetof(theClass, m_x##localClass); } #define CONNECTION_IID(iid) \ REFIID GetIID() { return iid; } #define END_CONNECTION_PART(localClass) \ } m_x##localClass; \ friend class X##localClass; 我们看到连接点对象从CConnectionPoint派生。宏CONNECTION_IID指定CConnectionPoin的虚函数成员返回出接口的IID。宏END_CONNECTION_PART定义了一个内嵌的成员m_x*.

CCmdTarget有一个内嵌的结构成员m_xConnPtContainer用于存放IConnectionPointContainer的虚表和偏移。 我们需要在接口映射表中加入 INTERFACE_PART(CSourceObj, IID_IConnectionPointContainer, ConnPtContainer)以使得源对象支持IConnectionPointContainer接口,我们早就谈到,这是客户判断一个对象是否源对象的标志。

5.2 事件激发 由于使用了IDispatch作为出接口,所以激发函数就是调用接收器的invoke函数。MFC提供了一个封装类COleDispatchDriver。它主要用于IDispatch接口的客户方调用操作。利用COleDispatchDriver的成员函数,客户可以创建自动化对象,也可以把COleDispatchDriver对象与某个自动化对象联系起来。更重要的是,它使得调用invoke函数的参数处理简单一些。

void CSourceObj::FirePropChanged (long nInt) { COleDispatchDriver driver; POSITION pos = m_xEventSetConnPt.GetStartPosition(); LPDISPATCH pDispatch; while (pos != NULL) {pDispatch = (LPDISPATCH) m_xEventSetConnPt.GetNextConnection(pos); ASSERT(pDispatch != NULL);//得到连接点对象所对应的出接口 driver.AttachDispatch(pDispatch, FALSE); //出接口和driver联系在一起 TRY driver.InvokeHelper(0/, DISPATCH_METHOD, VT_EMPTY, NULL,(BYTE *)(VTS_I4), nInt); //通过driver来调用invoke函数 END_TRY driver.DetachDispatch(); }

6 例子:用MFC实现源对象 新建MFC DLL工程SourceComp,选中automation 添加新类CSourceObj派生自CCmdTarget指定Create By type id 定义出接口IEventSet编辑odl文件.使用GUIDGen产生一个GUID 指定对象为源对象,且支持IEventSet coclass SourceObj { [default] dispinterface ISourceObj; [default,source] dispinterface IEventSet; }; 在CSourceObj的头文件中,加入连接点申明和连接点对象定义。 BEGIN_CONNECTION_PART(CSourceObj, EventSetConnPt) virtual REFIID GetIID(); END_CONNECTION_PART(EventSetConnPt) DECLARE_CONNECTION_MAP() 在CSourceObj的实现文件中构造函数中加入EnableConnections(); 加入IEventSet的IID定义 static const IID IID_IEventSet = { 0xb77c2985, 0x56dd, 0x11cf, { 0xb3, 0x55, 0x0, 0x10, 0x4b, 0x8, 0xcc, 0x22 } };

接口映射表中加入IConnectionPointContainer表项 INTERFACE_PART(CSourceObj, IID_IConnectionPointContainer, ConnPtContainer) 加入连接映射表的赋值 BEGIN_CONNECTION_MAP(CSourceObj, CCmdTarget) CONNECTION_PART(CSourceObj, IID_IEventSet, EventSetConnPt) END_CONNECTION_MAP() 实现虚函数GetIID REFIID CSourceObj::XEventSetConnPt::GetIID(void) { return IID_IEventSet; } XEventSetConnPt类继承自类CConnectionPoint,后者有一个纯虚的函数GetIID,所以必须给出实现。它返回它所支持的出接口的IID。

11。事件激发函数: void CSourceObj::FirePropChanged (long nInt) { COleDispatchDriver driver; POSITION pos = m_xEventSetConnPt.GetStartPosition(); LPDISPATCH pDispatch; while (pos != NULL) {pDispatch = (LPDISPATCH) m_xEventSetConnPt.GetNextConnection(pos); ASSERT(pDispatch != NULL);//得到连接点对象所对应的出接口 driver.AttachDispatch(pDispatch, FALSE);//出接口和driver联系在一起 TRY driver.InvokeHelper(0/, DISPATCH_METHOD, VT_EMPTY, NULL,(BYTE *)(VTS_I4), nInt); //通过driver来调用invoke函数,向所有连接点的接收器激发分发ID为0的事件。 END_TRY driver.DetachDispatch(); }} void CSourceObj::SetMyProperty(long nNewValue) { mProperty = nNewValue; FirePropChanged (mProperty);} 编译。注册。regsvr32

7 例子:用MFC实现接收器 1。新建MFC exe 基于dialoag box 2。AfxOleInit()初始化自动化。 3。加入成员变量IDispatch * m_pDispatch 以保存源对象的接口指针DWORD m_dwCookie以保存接收器与源对象的连接标识 4.对话框类也派生自CCmdTarget,给对话框类加入内嵌的接收器对象。也即对话框类也要实现IEventSet接口。 BEGIN_INTERFACE_PART(EventSink, IDispatch) INIT_INTERFACE_PART(CTestCtrlDlg, EventSink) STDMETHOD(GetTypeInfoCount)(unsigned int*); STDMETHOD(GetTypeInfo)(unsigned int, LCID, ITypeInfo**); STDMETHOD(GetIDsOfNames)(REFIID, LPOLESTR*, unsigned int, LCID, DISPID*); STDMETHOD(Invoke)(DISPID, REFIID, LCID, unsigned short, DISPPARAMS*, VARIANT*, EXCEPINFO*, unsigned int*); END_INTERFACE_PART(EventSink)

5。接收器对象对IUnkown的成员函数的实现和对IDispatch的除invoke外的成员函数的实现都可以简化处理。因为没有人会调用它们。 STDMETHODIMP CTestCtrlDlg::XEventSink::Invoke( DISPID dispid, REFIID, LCID, unsigned short wFlags, DISPPARAMS* pDispParams, VARIANT* pvarResult, EXCEPINFO* pExcepInfo, unsigned int* puArgError) { if (dispid == 0) AfxMessageBox("The Property has been changed!"); else AfxMessageBox("I don't known the event!"); return S_OK; } 对于分发ID为0的事件,invoke响应一个对话框。

7。创建源对象 GUID sourceobjCLSID; HRESULT hResult = ::CLSIDFromProgID(L"SourceComp.SourceObj", &sourceobjCLSID); if (FAILED(hResult)) return FALSE; hResult = CoCreateInstance(sourceobjCLSID, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch, (void **)&m_pDispatch); //创建源对象,获取源对象的IDispatch接口指针。 注意源对象 coclass SourceObj { [default] dispinterface ISourceObj; [default,source] dispinterface IEventSet; }; ISourceObj和IEventSet都是自动化接口。 此接口指针将要用来查询IConnectionPointContainer。

8。连接: static const IID IID_IEventSet ={……} int CTestCtrlDlg::Connection() { BOOL RetValue = 0; if (m_dwCookie != 0) return 2; LPCONNECTIONPOINTCONTAINER pConnPtCont; if ((m_pDispatch != NULL) &&SUCCEEDED(m_pDispatch->QueryInterface( IID_IConnectionPointContainer,(LPVOID*)&pConnPtCont))) // 查询IConnectionPointContainer接口。 { LPCONNECTIONPOINT pConnPt = NULL; DWORD dwCookie = 0; // 查询IID_IEventSet连接点对象 if (SUCCEEDED(pConnPtCont->FindConnectionPoint (IID_IEventSet, &pConnPt))) {pConnPt->Advise(&m_xEventSink, &dwCookie); // 进行连接,把自己的接收器对象传入。源对象将利用此指针激发事件。 m_dwCookie = dwCookie;//连接标志保存下来,供断开使用 RetValue = 1;pConnPt->Release(); } pConnPtCont->Release(); m_dwCookie = dwCookie; } return RetValue; }

9 断开: // 查询IConnectionPointContainer接口。 if ((m_pDispatch != NULL) &&SUCCEEDED(m_pDispatch->QueryInterface( IID_IConnectionPointContainer,(LPVOID*)&pConnPtCont))) {LPCONNECTIONPOINT pConnPt = NULL; // 查询IID_IEventSet连接点对象 if (SUCCEEDED(pConnPtCont->FindConnectionPoint(IID_IEventSet, &pConnPt))) {pConnPt->Unadvise(m_dwCookie); //使用连接标志断开。 pConnPt->Release(); m_dwCookie = 0; RetValue = 1; }

10 用户的设置属性操作。 void CTestCtrlDlg::OnSetproperty() { COleDispatchDriver driver; //由于源对象也是自动化对象,这里也使用COleDispatchDriver来操作自动化接口。将源对象的自动化接口与driver连起来。 driver.AttachDispatch(m_pDispatch, FALSE); TRY driver.SetProperty(0x1, VT_I4, m_Property); // 使用driver来调用自动化接口的属性。 END_TRY driver.DetachDispatch(); }