关于动态数据交换
Windows 提供了几种在应用程序之间传输数据的方法。 一种方法是使用动态数据交换 (DDE) 协议。 DDE 协议是一组消息和准则。 它会在共享数据的应用程序之间发送消息,并使用共享内存在应用程序之间交换数据。 应用程序可以使用 DDE 协议进行一次性数据传输,以及应用程序在由新数据可用时向另一个应用程序发送更新的连续交换。
Windows 还支持动态数据交换管理库 (DDEML)。 DDEML 是一个动态链接库 (DLL),应用程序可将其用于共享数据。 DDEML 提供函数和消息,可简化向应用程序添加 DDE 功能的任务。 应用程序使用 DDEML 函数来管理 DDE 对话,而不是直接发送、发布和处理 DDE 消息。 (DDE 对话是客户端和服务器应用程序之间的交互。)
DDEML 还提供用于管理 DDE 应用程序共享的字符串和数据的工具。 DDE 应用程序会创建和交换标识字符串的字符串句柄,以及标识内存对象的数据句柄,而不是使用原子和指向共享内存对象的指针。 DDEML 还使服务器应用程序能够注册其支持的服务名称。 这些名称将广播到系统中的其他应用程序,这些应用程序可以使用名称来连接到服务器。 此外,DDEML 会强制 DDE 应用程序以一致的方式实现 DDE 协议,以确保它们之间的兼容性。
使用基于消息的 DDE 协议的现有应用程序与使用 DDEML 的应用程序完全兼容。 也就是说,使用基于消息的 DDE 的应用程序可以建立对话,并通过利用 DDEML 的应用程序执行事务。 由于 DDEML 的许多优点,新应用程序应使用它,而不是 DDE 消息。 若要使用 DDEML 的 API 元素,必须在源文件中包含 DDEML 标头文件,与 DDEML 库链接,并确保 DDEML 动态链接库位于系统的搜索路径中。
以下是本节中要讨论的主题。
动态数据交换协议
由于 Windows 具有基于消息的体系结构,因此传递消息是自动在应用程序之间传输信息的最合适方法。 但是,消息仅包含两个参数(wParam 和 lParam),用于传递数据。 因此,当在应用程序之间传递多个字词的信息时,这些参数必须间接引用其他数据片段。 DDE 协议确切地定义了应用程序应如何使用 wParam 和 lParam 参数,通过全局原子和共享内存句柄传递较大的数据片段。 DDE 协议具有用于分配和删除全局原子和共享内存对象的特定规则。
全局原子是对字符串的引用。 在 DDE 协议中,原子标识交换数据的应用程序、要交换的数据的性质以及数据项本身。 有关原子的详细信息,请参阅关于原子。
用于 Windows 动态数据交换
DDE 最适用于不需要持续用户交互的数据交换。 通常,应用程序为用户提供一种方法,用于在交换数据的应用程序之间建立链接。 但是,建立链接后,应用程序无需用户进一步参与即可交换数据。
DDE 可用于实现广泛的应用程序功能,例如:
- 链接到实时数据,例如股票市场更新、科学仪器或流程控制。
- 创建复合文档,例如包含图形应用程序生成的图表的字处理文档。 使用 DDE 时,图表将在更改源数据时更改,而文档的其余部分保持不变。
- 在应用程序之间执行数据查询,例如,电子表格在数据中查询逾期帐户。
用户角度的动态数据交换
以下示例从用户的角度演示了两个 DDE 应用程序如何协同工作。
电子表格用户希望使用 Microsoft Excel 跟踪纽约证券交易所特定股票的价格。 用户有一个名为“报价”的应用程序,该应用程序反过来又有权访问 NYSE 数据。 Excel 和“对话”之间的 DDE 对话如下所示:
- 用户通过提供应用程序的名称(“报价”)来启动对话,该应用程序将提供数据以及相关特定主题 (NYSE)。 生成的 DDE 对话用于请求特定股票的报价。
- Excel 将应用程序和主题名称广播到系统中当前正在运行的所有 DDE 应用程序。 “报价”做出响应,从而与 Excel 就 NYSE 主题建立对话。
- 接着,用户可以在单元格中创建电子表格公式,以请求每当特定股票报价更改时都自动更新电子表格。 例如,用户可以指定 Excel 公式 ='Quote'|'NYSE'!ZAXX,请求每当 ZAXX 股票售价发生更改时都自动更新
- 用户可以随时终止 ZAXX 股票报价的自动更新。 其他单独建立的数据链接(如其他股票的报价)仍将在同一 NYSE 对话下保持活动状态。
- 用户还可以终止 Excel 和 Quote 之间有关 NYSE 的整个对话,以便无需启动新对话即可建立有关该主题的特定数据链接。
动态数据交换概念
以下几个部分介绍了是了解动态数据交换的关键的概念和术语。
客户端、服务器和对话
据称有两个参与 DDE 的应用程序参与了 DDE 对话。 启动对话的应用程序是 DDE 客户端应用程序;响应客户端的应用程序是 DDE 服务器应用程序。 应用程序可以同时参与多个对话,以在某些对话中充当客户端,而在其他对话中充当服务器。
DDE 对话发生在两个窗口之间,每个参与的应用程序各一个。 窗口可能是应用程序的主窗口;与特定文档关联的窗口,如多文档界面 (MDI) 应用程序中所示;或隐藏的(不可见)窗口,其唯一用途是处理 DDE 消息。
由于 DDE 对话由参与对话的窗口的句柄对标识,因此不应让任何窗口与另一个窗口进行多个对话。 客户端应用程序或服务器应用程序必须为每个与特定服务器或客户端应用程序建立的对话提供不同的窗口。
通过为每个对话创建隐藏窗口,应用程序可以确保一对客户端和服务器窗口永远不会参与多个对话。 此窗口的唯一用途是处理 DDE 消息。
应用程序名称、主题名称和项名称
DDE 协议使用应用程序名称、主题名称和项名称的三级层次结构标识在客户端和服务器之间传递的数据单位。
每个 DDE 对话都由应用程序名称和主题唯一定义。 在 DDE 对话的开头,客户端和服务器确定应用程序名称和主题。 应用程序名称通常是服务器应用程序的名称。 例如,当 Excel 充当对话中的服务器时,应用程序名称为 Excel。
DDE 主题是数据的一般分类,其中可以在对话期间“讨论”(交换)多个数据项。 对于操作基于文件的文档的应用程序,主题通常是文件名。 对于其他应用程序,主题是特定于应用程序的名称。
由于客户端和服务器窗口句柄共同标识 DDE 对话,因此在对话过程中无法更改定义对话的应用程序名称和主题。
DDE 数据项是与在应用程序之间交换的对话主题相关的信息。 可将数据项的值从服务器传递到客户端,也可以从客户端传递到服务器。 可以使用任何标准剪贴板格式或已注册剪贴板格式传递数据。 名为“链接”的特殊已注册格式将标识 DDE 对话中的项。 有关剪贴板格式的详细信息,请参阅剪贴板。
系统主题
应用程序应始终支持系统主题。 本主题提供另一个应用程序通常感兴趣的信息的上下文。
数据项值必须以 CF_TEXT 剪贴板格式呈现。 必须使用制表符分隔系统主题项值的单个元素。 下表建议系统主题的一些项。
项 | 说明 |
---|---|
格式 | 应用程序可以呈现的剪贴板格式的制表符分隔列表。 通常,列出的 CF_ 格式会删除名称的“CF_”部分(例如,CF_TEXT 列为“TEXT”)。 |
帮助 | 简要说明如何使用 DDE 服务器的文本。 |
ReturnMessage | 支持最近使用的 WM_DDE_ACK 消息的详细信息。 当需要超过八位应用程序特定的返回数据时,此项非常有用。 |
Status | 指示应用程序的当前状态。 当服务器收到此系统主题项的 WM_DDE_REQUEST 消息时,应根据情况发布带含“忙碌”或“就绪”的字符串的 WM_DDE_DATA 消息来作为响应。 |
SysItems | 应用程序支持的系统主题项列表。 |
TopicItemList | 与 SysItems 项类似,不同之处在于,只是系统主题以外的每个主题都应支持 TopicItemList。 这允许浏览任何主题下支持的项。 如果无法枚举项,则此项应仅包含“TopicItemList”。 |
主题 | 应用程序当前支持的主题列表;此列表可能会因时刻而异。 |
永久数据链接
DDE 对话开始后,客户端可以与服务器建立一个或多个永久数据链接。 数据链接是一种通信机制,每当指定数据项的值发生更改时,服务器就会使用该机制来通知客户端。 数据链接是永久性的,即此通知进程将继续至数据链接或 DDE 对话本身终止。
有两种类型的永久 DDE 数据链接:暖和热。 在暖数据链接中,服务器通知客户端数据项值已更改,但服务器在客户端请求之前不会向客户端发送数据值。 在热数据链接中,服务器会立即将更改的数据值发送给客户端。
支持暖或热数据链接的应用程序通常会在其“编辑”菜单中提供“复制链接”或“粘贴链接”命令,使用户能够在应用程序之间建立链接。
原子和共享内存对象
DDE 消息的某些参数是全局原子或共享内存对象。 使用这些参数的应用程序必须遵循有关何时分配和删除这些参数的显式规则。 在所有情况下,消息发送方都必须删除预期接收方由于错误条件(如 PostMessage 函数失败)而不会接收的任何原子或共享内存对象。
DDE 将共享内存对象用于三个目的:
- 携带要交换的数据项值。 这是由 WM_DDE_DATA 和 WM_DDE_POKE 消息中的 hData 参数引用的项。
- 在消息中携带选项。 这是 WM_DDE_ADVISE 消息中的 hOptions 参数引用的项。
- 携带命令执行字符串。 这是由 WM_DDE_EXECUTE 消息及其对应的 WM_DDE_ACK 消息中的 hCommands 参数引用的项。
接收 DDE 共享内存对象的应用程序必须将其视为只读。 应用程序不得将对象用作相互读写区域来自由交换数据。
与 DDE 原子一样,应用程序应释放共享内存对象,以有效管理内存。 应用程序还应锁定和解锁内存对象。
动态数据交换消息概述
由于 DDE 是基于消息的协议,因此它不使用任何函数或库。 所有 DDE 事务都通过在客户端和服务器窗口之间传递某些定义的 DDE 消息来执行。
有九条 DDE 消息;这些消息的符号常量在 DDE 标头文件中定义。 此标头文件中还定义了各种 DDE 消息的某些结构。
下表概述了 DDE 消息。
消息 | 说明 |
---|---|
WM_DDE_ACK | 确认收到或未收到消息。 |
WM_DDE_ADVISE | 请求服务器应用程序每当数据项发生更改时都提供更新或通知。 这会建立永久数据链接。 |
WM_DDE_DATA | 向客户端应用程序发送数据项值。 |
WM_DDE_EXECUTE | 将字符串发送给服务器应用程序,服务器应用程序应将字符串作为一系列命令进行处理。 |
WM_DDE_INITIATE | 启动客户端和服务器应用程序之间的对话。 |
WM_DDE_POKE | 向服务器应用程序发送数据项值。 |
WM_DDE_REQUEST | 请求服务器应用程序提供数据项的值。 |
WM_DDE_TERMINATE | 终止对话。 |
WM_DDE_UNADVISE | 终止永久数据链接。 |
应用程序调用 SendMessage 来发出发送的 WM_DDE_INITIATE 消息或 WM_DDE_ACK,以便响应 WM_DDE_INITIATE。 所有其他消息都由 PostMessage 发送。 这些调用的第一个参数是接收窗口的句柄;第二个参数包含要发送的消息;第三个参数标识发送窗口;第四个参数包含消息特定参数。
动态数据交换消息流
典型的 DDE 对话包含以下事件:
客户端应用程序启动对话,并且服务器应用程序会作出响应。
应用程序通过以下任一或全部方法交换数据:
-
- 服务器应用程序在客户端请求时将数据发送给客户端。
- 客户端应用程序将未请求的数据发送给服务器应用程序。
- 客户端应用程序请求服务器应用程序在数据项发生更改时通知客户端(暖数据链接)。
- 客户端应用程序请求服务器应用程序在数据发生更改时发送数据(热数据链接)。
- 服务器应用程序在客户端请求时执行命令。
-
客户端或服务器应用程序终止对话。
处理来自客户端或服务器的请求的应用程序窗口必须严格按照收到的顺序处理请求。
客户端可以与多个服务器建立对话;服务器可以与多个客户端进行对话。 处理来自多个源的消息时,客户端或服务器必须同步处理对话的消息,但不需要同步处理所有消息。 换言之,它可以根据需要从一个对话转移到另一个对话。
如果应用程序因正在等待 DDE 响应而无法处理传入请求,则必须发布 WM_DDE_ACK 消息,其中将 DDEACK 结构的 fBusy 成员设置为 1,以防止出现死锁。 如果出于任何原因,应用程序无法在合理的时间内处理传入请求,则应用程序还可以忙碌 WM_DDE_ACK 消息。
应用程序应能够处理客户端或服务器在特定时间内无法响应消息的情况。 由于超时间隔可能会因应用程序的性质和用户系统的配置(包括是否连接到网络)而有所不同,因此应用程序应为用户提供指定间隔的方法。
参数打包函数
许多 DDE 消息的 lParam 参数都包含两项数据。 例如,WM_DDE_DATA 消息的 lParam 包含数据句柄和原子。 应用程序必须使用 PackDDElParam 函数将句柄和原子打包到 lParam 参数中,并使用 UnpackDDElParam 函数删除值。 DDE 应用程序必须使用 PackDDElParam 和 UnpackDDElParam 处理在 DDE 对话期间发布的所有消息。
应用程序还可以使用 ReuseDDElParam 和 FreeDDElParam 函数。 ReuseDDElParam 允许 DDE 应用程序重复使用打包的 lParam 参数,从而帮助减少应用程序必须在对话期间必须的内存重新分配数。 应用程序可以使用 FreeDDElParam 释放与在 DDE 对话期间收到的数据句柄关联的内存。
动态数据交换和模拟
若要允许服务器模拟客户端,客户端应调用 DdeSetQualityOfService 函数。 SECURITY_IMPERSONATION_LEVEL 结构用于控制服务器可能执行的模拟级别。
DDE 服务器可以调用 ImpersonateDdeClientWindow 函数来模拟 DDE 客户端。 DDEML 服务器应使用 DdeImpersonateClient 函数。