第九章 统一数据传输
1 、概述
COM 提供了应用之间数据交换的标准方案,称为统一数据传输( UDT , uniform data transfer )。统一数据传输技术建立在结构化存储技术的基础之上,它通过一个“数据对象”来表达要传输的数据信息,因此,统一数据传输技术的核心在于数据对象的定义和实现。剪贴板和拖-放是统一数据传输的两个典型应用。
2 、数据交换标准
早期的 OLE1.0 版本使用 DDE ( Dynamic Data Exchange ,动态数据交换)作为数据交换标准,但 DDE 使用的格式比较简单,并且只能用全局内存作为传输介质,所以传输的效率和功能都受限制。 OLE2.0 版本引入了 COM 作为其基本的结构模型,使用 COM 提供的统一数据传输作为其数据交换机制,可直接在不同介质之间进行传输。
3 、数据交换与传输协议的分离
在不同应用之间进行数据传输操作包括两方面的内容,首先是数据格式的统一,其次是传输协议的建立。
以前采用 DDE 方式只能使用简单的数据结构对信息进行描述, COM 的统一数据传输机制使用“数据对象”作为信息实体,数据对象通过 IDataObject 接口暴露其内部信息。由于数据对象本身是一个 COM 对象,因此它不仅可以表达一般的结构化信息,也可以表达一些非结构化信息,甚至是动态信息。 IDataObject 接口为应用程序进行数据传输建立了标准。在 Windows 平台上,最基本的传输协议为剪贴板、拖-放,应用程序通常利用这两种协议获得数据对象。
在统一数据机制引入到 Windows 系统之前, Windows 提供了许多 API 函数以及预定义的消息用于应用之间传输数据的桥梁。这些 API 函数把传输协议和传输数据信息绑在一起,比如用于处理剪贴板数据传送的一组函数: GetClipboardData 、 SetClipboardData 和 CloseClipboard 等,而 DDE 则通过发送消息作为数据传送的手段。
COM 提供的统一数据传输机制可很好地避免 Windows API 函数的数据传输的限制,一方面它定义了两个数据结构 FORMATETC 和 STGMEDIUM ,分别用来描述数据格式和存储介质,使新的机制可适应更广泛的数据类型和存储介质;另一方面它为数据对象提供了“数据表化通知”的机制。
数据对象的概念使统一数据传输机制不仅可用于应用之间传输数据,也可以成为组件程序之间的信息交换标准。
4 、剪贴板
剪贴板是一个全系统共享的数据缓冲区,每个应用都可以通过系统提供的 API 函数访问剪贴板。它的三个标准操作是:剪切、复制、粘贴。
Windows 系统在引入 COM 的统一数据传输机制之前,提供了一组 API 函数以及预定义的 CF_*** 标准格式,这些格式包括文本类型、位图类型、图元文件( metafile )数据等,而且这些数据必须存放在全局内存中。但剪贴板技术与数据对象结合之后,情况有了很大的变化,可用于传输数据对象,比如 OLE 文档对象、 ActiveX 控制对象等,或者是应用程序中自定义的数据对象,只要此对象实现 IDataObject 接口即可。剪贴板成为数据对象的提供方和接收方之间的通信协议,而且这种通信方式是异步进行的。
5 、拖-放
拖-放技术也是基本的传输协议,它的使用方式与剪贴板有所不同,程序采用同步的方式进行。
6 、数据结构 FORMATETC 和 STGMEDIUM
FORMATETC 结构定义了用于传输的数据格式,它扩充了基本的剪贴板数据格式; STGMEDIUM 结构定义了用于传输的介质类型,它即可以描述常用的全局内存,也可以描述其他的存储介质。
7 、数据对象和 IDataObject 接口
统一数据传输中的数据对象是一个 COM 对象,它实现了 IDataObject 接口。在数据对象的实现方(即提供方)和客户(即接收方)之间, IDataObject 接口为两者建立了标准,而各种传输协议如剪贴板和拖-放等,它们所传递的实际上是 IDataObject 接口指针。
8 、通报连接机制
为了实现从数据对象到客户程序的通信过程,要求数据对象在状态发生变化时能主动通知客户程序,而客户程序必须提供一个接收器对象以便接收这些通知,这是曾在第六章中介绍过的 COM 提供的通过接收器对象建立的通用的双向通信机制。而在统一数据传输机制中使用的通知接收器要简单一些,它是由客户程序实现的内部对象,只需实现 IAdviseSink 接口。
数据对象与客户程序之间的连接方式较第六章中的可连接对象机制简便得多。
客户程序通过 IDataObject::DAdvise 函数建立通报连接时,它除了要提供接收器对象的 IAdciseSink 接口指针,还要提供两个信息:格式信息和与通报连接有关的标志信息。
COM 提供了“数据通报控制器”( data advise holder )的内部对象,数据通报控制器对象实现了 IDataAdviseHolder 接口。数据对象利用 COM API 函数 CreateDataAdviseHolder 创建一个数据通报控制器对象,该函数返回对象的 IDataAdviseHolder 接口指针,然后,数据对象把 IDataObject 接口的三个与变化通知有关的成员函数直接委托给数据通报控制器对象 IDataAdviseHolder 接口的相应成员函数。
9 、数据对象
在实现数据对象时,不管是进程内组件程序,还是进程外组件程序,由于与数据对象有关的接口都是 COM 定义的标准接口, COM 已经提供了这些接口的跨进程列集器,所以不用考虑跨进程的细节。进程模型只影响性能因素。
10 、通过剪贴板传输数据
在剪贴板与数据对象结合起来之前, Win32 中为支持剪贴板操作而提供了一组 API 函数: OpenClipboard 、 CloseClipboard 、 EmptyClipboard 、 SetClipboardData 、 GetClipboardData 、 IsClipboardFormatAvailable 、 EnumClipboardFormats 。
因为剪贴板是 Windows 操作系统支持的特性,而 COM 是与平台无关的组件对象模型规范,所以剪贴板和数据对象结合之后的特性不属于 COM 范畴,严格来讲应该数 OLE 技术的一部分,称之为 OLE 剪贴板。 OLE 也提供了四个封装过的 API 函数: OleSetClipboard 、 OleGetClipnoard 、 OleFlushClipboard 、 OleCurrentClipboard 。
11 、拖-放数据传输协议
与剪贴板传输协议不同, OLE 的拖-放数据传输协议只能传输数据对象,而不能传输其他的数据。
拖-放操作是一个与界面有关的数据传输过程,通常涉及到源窗口和目标窗口。
OLE 提供的 API 函数 RegisterDragDrop 把“放目标”对象与窗口联系起来。
12 、 MFC 对剪贴板和拖-放的支持
MFC 提供了两个类用于支持数据对象,分别为 COleDataSource 和 COleDataObject 类。这两个类直接继承于 CCmdTarget ,其中 COleDataSource 类用于源程序一方, COleDataObject 用于客户程序一方。
COleDataSource 类实现了 IDataObject 接口,但是不支持通报连接。 COleDataSource 类创建并管理了一组数据格式,这些数据格式被保存在对象内部(缓冲区中)。 COleDataSource 类实现了一个真正的 COM 对象,它有自己的引用计数。