关于动态数据交换

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 具有基于消息的体系结构,因此传递消息是自动在应用程序之间传输信息的最合适方法。 但是,消息仅包含两个参数(wParamlParam),用于传递数据。 因此,当应用程序之间传递多个字词的信息时,这些参数必须间接引用其他数据片段。 DDE 协议确切地定义了应用程序应如何使用 wParamlParam 参数通过全局原子和共享内存句柄传递较大的数据片段。 DDE 协议具有用于分配和删除全局原子和共享内存对象的特定规则。

全局原子是对字符串的引用。 在 DDE 协议中,原子标识交换数据的应用程序、要交换的数据的性质以及数据项本身。 有关原子的详细信息,请参阅 关于 Atoms

用于 Windows 动态数据交换

DDE 最适合不需要持续用户交互的数据交换。 通常,应用程序为用户提供一种方法,用于在交换数据的应用程序之间建立链接。 但是,建立该链接后,应用程序无需进一步参与即可交换数据。

DDE 可用于实现广泛的应用程序功能,例如:

  • 链接到实时数据,例如股票市场更新、科学仪器或流程控制。
  • 创建复合文档,例如包含图形应用程序生成的图表的字处理文档。 使用 DDE 时,图表将在更改源数据时更改,而文档的其余部分保持不变。
  • 在应用程序之间执行数据查询,例如,电子表格查询过期帐户的数据库。

从用户的角度来看,动态数据交换

下面的示例演示了两个 DDE 应用程序如何协同工作,如用户的观点所示。

电子表格用户希望使用 Microsoft Excel 来跟踪纽约证券交易所特定股票的价格。 用户有一个名为 Quote 的应用程序,反过来又有权访问 NYSE 数据。 Excel 和 Quote 之间的 DDE 对话如下所示:

  • 用户通过提供应用程序的名称(Quote)来启动对话,该应用程序的名称将提供数据以及感兴趣的特定主题(NYSE)。 生成的 DDE 对话用于请求特定股票的报价。
  • Excel 将应用程序和主题名称广播到系统中当前运行的所有 DDE 应用程序。 条目回应,与 Excel 就 NYSE 主题进行对话。
  • 然后,用户可以在单元格中创建电子表格公式,该公式要求每当特定股票报价更改时自动更新电子表格。 例如,用户可以通过指定以下 Excel 公式(='Quote'|' )在 ZAXX 股票的销售价格发生更改时请求自动更新: ='Quote'|'NYSE'!ZAXX
  • 用户可以随时终止 ZAXX 股票报价的自动更新。 其他单独建立的数据链接(如其他股票报价)仍将在同一纽约证交所对话下保持活跃状态。
  • 用户还可以终止 Excel 与 Quote 在 NYSE 主题上的整个对话,这样就无法在未启动新对话的情况下建立该主题的特定数据链接。

动态数据交换概念

以下部分介绍了了解动态数据交换的关键概念和术语。

客户端、服务器和对话

据说有两个参与 DDE 的应用程序参与 DDE 对话。 启动会话的应用程序是 DDE 客户端应用程序;响应客户端的应用程序是 DDE 服务器应用程序。 应用程序可以同时参与多个对话,充当某些会话中的客户端,并充当其他会话中的服务器。

DDE 对话发生在两个窗口之间,每个参与的应用程序各有一个。 窗口可能是应用程序的主窗口;与特定文档关联的窗口,如多文档界面 (MDI) 应用程序中所示;或隐藏的(不可见)窗口,其唯一用途是处理 DDE 消息。

由于 DDE 会话由参与会话的窗口句柄对标识,因此任何窗口都不应与另一个窗口同时进行多个会话。 客户端应用程序或服务器应用程序必须为每个与特定服务器或客户端应用程序的对话提供不同的窗口。

应用程序可以通过为每个会话创建隐藏窗口来确保一对客户端和服务器窗口永远不会参与多个会话。 此窗口的唯一用途是处理 DDE 消息。

应用程序、主题和项名称

DDE 协议使用应用程序、主题和项名称的三级层次结构标识客户端和服务器之间传递的数据单位。

每个 DDE 会话都由应用程序名称和主题唯一定义。 在 DDE 会话的开头,客户端和服务器确定应用程序名称和主题。 应用程序名称通常是服务器应用程序的名称。 例如,当 Excel 充当对话中的服务器时,应用程序名称为 Excel。

DDE 主题是数据的一般分类,其中多个数据项可以在对话期间“讨论”(交换)。 对于对基于文件的文档进行作的应用程序,该主题通常是文件名。 对于其他应用程序,主题是应用程序特定的名称。

由于客户端和服务器窗口共同处理标识 DDE 对话,因此在会话过程中无法更改定义会话的应用程序名称和主题。

DDE 数据项是与应用程序之间交换的对话主题相关的信息。 可将数据项的值从服务器传递到客户端,也可以从客户端传递到服务器。 可以使用任何标准剪贴板格式或已注册剪贴板格式传递数据。 名为 Link 的特殊注册格式标识 DDE 对话中的项。 有关剪贴板格式的详细信息,请参阅 剪贴板

系统主题

应用程序应随时支持系统主题。 本主题提供信息的上下文,这些信息可能对另一个应用程序普遍感兴趣。

数据项值必须以 CF_TEXT 剪贴板格式呈现。 系统主题的项值的单个元素必须由制表符分隔。 下表建议了一些系统主题的内容。

条目 DESCRIPTION
格式 应用程序可以呈现的剪贴板格式列表,按制表符分隔。 通常,CF_ 格式在列出时会去掉名称中的“CF_”部分(例如,CF_TEXT 被列为“TEXT”)。
帮助 简要说明如何使用 DDE 服务器的文本。
返回信息 关于最近使用的 WM_DDE_ACK 消息的支持性详细信息。 当需要超过八位特定于应用程序的返回数据时,此项非常有用。
状态 指示应用程序的当前状态。 当服务器收到对这个系统主题项的 WM_DDE_REQUEST 消息时,应通过发布一个包含“忙碌”或“就绪”状态字符串的 WM_DDE_DATA 消息来响应。
SysItems 应用程序支持的系统主题项列表。
主题项目列表 与 SysItems 项类似,不同之处在于,除了系统主题以外的每个主题都应支持 TopicItemList。 这允许浏览任何主题下支持的项目。 如果无法枚举项,则此项应仅包含“TopicItemList”。
主题 应用程序当前支持的主题列表;此列表可能因时刻而异。

DDE 会话开始后,客户端可以与服务器建立一个或多个永久数据链接。 数据链接是一种通信机制,每当指定数据项的值发生更改时,服务器就通知客户端。 数据链接是永久性的,即此通知过程将继续,直到数据链接或 DDE 会话本身终止。

有两种类型的持久 DDE 数据链接:温链接和热链接。 在暖数据链接中,服务器通知客户端数据项的值已更改,但服务器在客户端请求数据之前不会向客户端发送数据值。 在热数据链接中,服务器会立即将更改的数据值发送到客户端。

支持暖数据链接或热数据链接的应用程序通常在其“编辑”菜单中提供“复制”或“粘贴链接”命令,使用户能够在应用程序之间建立链接。

Atom 和共享内存对象

DDE 消息的某些参数是全局原子或共享内存对象。 使用这些参数的应用程序必须遵循有关何时分配和删除这些参数的显式规则。 在所有情况下,消息发送方都必须删除预期接收方由于错误条件(如 PostMessage 函数失败)而不会接收的任何原子或共享内存对象。

DDE 将共享内存对象用于三个目的:

  • 要交换数据项值。 这是由WM_DDE_DATAWM_DDE_POKE消息中的 hData 参数引用的项。
  • 在信息中携带选项。 这是由 hOptions 参数在 WM_DDE_ADVISE 消息中引用的项。
  • 携带命令执行字符串。 这是hCommands参数在WM_DDE_EXECUTE消息及其对应的WM_DDE_ACK消息中引用的项。

接收 DDE 共享内存对象的应用程序必须将其视为只读。 应用程序不得将对象用作相互读写区域,以自由交换数据。

与 DDE 原子一样,应用程序应释放共享内存对象以有效管理内存。 应用程序还应锁定和解锁内存对象。

动态数据交换消息概述

由于 DDE 是基于消息的协议,因此它不使用任何函数或库。 所有 DDE 事务都通过传递客户端和服务器窗口之间的某些定义的 DDE 消息来执行。

有九条 DDE 消息;这些消息的符号常量在 DDE 头文件中定义。 此头文件中还定义了各种 DDE 消息的某些结构。

下表汇总了 DDE 消息:

消息 DESCRIPTION
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 对话包含以下事件:

  1. 客户端应用程序启动会话,服务器应用程序响应。
  2. 应用程序通过以下任一或全部方法交换数据:
    • 服务器应用程序在客户端的请求中将数据发送到客户端。
    • 客户端应用程序将未经请求的数据发送到服务器应用程序。
    • 客户端应用程序请求服务器应用程序在数据项发生更改时通知客户端(暖数据链接)。
    • 客户端应用程序请求服务器应用程序在数据发生更改时发送数据(热数据链接)。
    • 服务器应用程序在客户端的请求中执行命令。
  3. 客户端或服务器应用程序终止会话。

处理来自客户端或服务器的请求的应用程序窗口必须严格按照接收的顺序处理它们。

客户端可以与多个服务器建立对话;服务器可以与多个客户端进行对话。 处理来自多个源的消息时,客户端或服务器必须同步处理会话的消息,但不需要同步处理所有消息。 换句话说,它可以根据需要从一个对话转移到另一个对话。

如果应用程序由于正在等待 DDE 响应而无法处理传入请求,则必须发布一条 WM_DDE_ACK 消息,同时将 DDEACK 结构中的 fBusy 成员设置为 1,以防止死锁。 如果出于任何原因,应用程序无法在合理的时间内处理传入请求,则应用程序还可以发送繁忙的 WM_DDE_ACK 消息。

应用程序应能够处理客户端或服务器在特定时间内响应消息的失败。 由于超时间隔可能因应用程序的性质和用户系统的配置(包括是否连接到网络)而有所不同,因此应用程序应为用户提供指定间隔的方法。

参数打包函数

许多 DDE 消息的 lParam 参数包含两段数据。 例如,WM_DDE_DATA消息的 lParam 包含数据句柄和原子。 应用程序必须使用 PackDDElParam 函数将句柄和原子打包到 lParam 参数中,并使用 UnpackDDElParam 函数删除值。 DDE 应用程序必须在 DDE 对话期间发布的所有消息中使用 PackDDElParamUnpackDDElParam

应用程序还可以使用 ReuseDDElParamFreeDDElParam 函数。 ReuseDDElParam 允许 DDE 应用程序重复使用打包 的 lParam 参数,帮助减少应用程序在会话期间必须执行的内存重新分配数。 应用程序可以使用 FreeDDElParam 释放与在 DDE 对话期间收到的数据句柄关联的内存。

动态数据交换和身份模仿

若要允许服务器模拟客户端,客户端将调用 DdeSetQualityOfService 函数。 SECURITY_IMPERSONATION_LEVEL结构用于控制服务器可能执行的模拟级别。

DDE 服务器可以通过调用 ImpersonateDdeClientWindow 函数来模拟 DDE 客户端。 DDEML 服务器应使用 DdeImpersonateClient 函数。

剪贴板

SendMessage