事务管理(数据交换)

与服务器建立会话后,客户端可以发送事务,以从服务器获取数据和服务。

以下几个主题介绍客户端可用于与服务器交互的事务类型。

请求事务

客户端应用程序可以使用 XTYP_REQUEST 事务从服务器应用程序请求数据项。 客户端会调用 DdeClientTransaction 函数,将 XTYP_REQUEST 指定为事务类型,并指定应用程序需要的数据项。

动态数据交换管理库 (DDEML) 将 XTYP_REQUEST 事务传递给服务器,从而指定客户端请求的主题名称、项名称和数据格式。 如果服务器支持请求的主题、项和格式,则服务器应返回标识项的当前值的数据句柄。 DDEML 将此句柄传递给客户端,作为 DdeClientTransaction 的返回值。 如果服务器不支持请求的主题、项或格式,则服务器应返回 NULL

DdeClientTransaction 使用 lpdwResult 参数将事务状态标志返回给客户端。 如果服务器未处理 XTYP_REQUEST 事务,DdeClientTransaction 会返回 NULL,并且 lpdwResult 指向 DDE_FNOTPROCESSED 或 DDE_FBUSY 标志。 如果返回 DDE_FNOTPROCESSED 标志,客户端无法确定服务器未处理事务的原因。

如果服务器不支持 XTYP_REQUEST 事务,则应在 DdeInitialize 函数中指定 CBF_FAIL_REQUESTS 筛选器标志。 此标志可防止 DDEML 将事务发送给服务器。

戳击事务

客户端可以使用 DdeClientTransactionXTYP_POKE 事务发送给服务器的回调函数,以将未请求的数据发送给服务器。

客户端应用程序首先创建一个缓冲区,其中包含要发送到服务器的数据,然后将指向缓冲区的指针作为参数传递给 DdeClientTransaction。 或者,客户端可以使用 DdeCreateDataHandle 函数获取标识数据的数据句柄,然后将句柄传递给 DdeClientTransaction。 在任一情况下,在调用 DdeClientTransaction 时,客户端还会指定主题名称、项名称和数据格式。

DDEML 将 XTYP_POKE 事务传递给服务器,从而指定客户端请求的主题名称、项名称和数据格式。 若要接受数据项和格式,服务器应返回 DDE_FACK。 若要拒绝数据,服务器应返回 DDE_FNOTPROCESSED。 如果服务器太忙而无法接受数据,则服务器应返回 DDE_FBUSY。

DdeClientTransaction 返回时,客户端可以使用 lpdwResult 参数访问事务状态标志。 如果标志是 DDE_FBUSY,客户端应稍后再次发送事务。

如果服务器不支持 XTYP_POKE 事务,则应在 DdeInitialize 中指定 CBF_FAIL_POKES 筛选器标志。 此标志可防止 DDEML 将此事务发送给服务器。

建议事务

客户端应用程序可以使用 DDEML 建立一个或多个指向服务器应用程序中项的链接。 建立此类链接后,服务器会将有关链接项的定期更新发送给客户端(通常,每当与服务器应用程序关联的项值发生更改时都会发送)。 链接会在两个应用程序之间建立建议循环,此循环会一直保留到客户端将其结束。

有两种建议循环:“热”和“暖”。在热建议循环中,服务器会立即发送标识已更改值的数据句柄。 在暖建议循环中,服务器会通知客户端项值已更改,但在客户端请求之前不会发送数据句柄。

客户端可以在调用 DdeClientTransaction 时指定 XTYP_ADVSTART 事务类型,以请求服务器热建议循环。 若要请求暖建议循环,客户端必须将 XTYPF_NODATA 标志与 XTYP_ADVSTART 事务类型组合。 在任一事件中,DDEML 都会将 XTYP_ADVSTART 事务传递给服务器的动态数据交换 (DDE) 回调函数。 服务器的 DDE 回调函数应检查 XTYP_ADVSTART 事务随附的参数(包括请求的格式、主题名称和项名称),然后返回 TRUE 以允许建议循环,或返回 FALSE 以拒绝建议循环。

建立建议循环后,每当与所请求项名称关联的项的值发生更改时,服务器应用程序都应调用 DdePostAdvise 函数。 此调用会导致将 XTYP_ADVREQ 事务发送给服务器自己的 DDE 回调函数。 服务器的 DDE 回调函数应返回标识数据项新值的数据句柄。 然后,DDEML 会向客户端的 DDE 回调函数发送 XTYP_ADVDATA 事务,以通知客户端指定项已更改。

如果客户端请求了热建议循环,DDEML 会在 XTYP_ADVDATA 事务期间将已更改项的数据句柄传递给客户端。 否则,客户端可以发送 XTYP_REQUEST 事务来获取该数据句柄。

服务器发送更新的速度可能会比客户端处理新数据的速度快。 对于必须对数据执行长时间处理操作的客户端来说,更新速度可能是个问题。 在这种情况下,客户端应在请求建议循环时指定 XTYPF_ACKREQ 标志。 在服务器发送下一个数据项之前,此标志会促使服务器等待客户端确认已收到并处理了数据项。 使用 XTYPF_ACKREQ 标志建立的建议循环对于快速服务器而言会更可靠,但有时可能会错过更新。 只要客户端与服务器保持同步,就可以保证在不使用 XTYPF_ACKREQ 标志的情况下建立的建议循环不会错过更新。

客户端可以在调用 DdeClientTransaction 时指定 XTYP_ADVSTOP 事务类型,以结束建议循环。

如果服务器不支持建议循环,则应在 DdeInitialize 函数中指定 CBF_FAIL_ADVISES 筛选器标志。 此标志可防止 DDEML 将 XTYP_ADVSTARTXTYP_ADVSTOP 事务发送到服务器。

执行事务

客户端可以使用 XTYP_EXECUTE 事务促使服务器执行一个命令或一系列命令。

若要执行服务器命令,客户端应先创建一个缓冲区,该缓冲区包含服务器要执行的命令字符串,然后在调用 DdeClientTransaction 时,传递指向缓冲区的指针或标识缓冲区的数据句柄。 其他必需参数包括对话句柄、项名称字符串句柄、格式规范和 XTYP_EXECUTE 事务类型。 若应用程序要创建用于传递执行数据的数据句柄,则必须为 DdeCreateDataHandle 函数的 hszItem 参数指定 NULL,并为 uFmt 参数指定零。

DDEML 会将 XTYP_EXECUTE 事务传递给服务器的 DDE 回调函数,并指定标识命令字符串的格式名称、对话句柄、主题名称和数据句柄。 如果服务器支持该命令,则应使用 DdeAccessData 函数获取指向命令字符串的指针,执行命令,然后返回 DDE_FACK。 如果服务器不支持该命令或无法完成事务,则应返回 DDE_FNOTPROCESSED。 如果服务器太忙而无法完成事务,则应返回 DDE_FBUSY。

通常,服务器的回调函数在返回之前应会处理 XTYP_EXECUTE 事务,但显示以下异常:

  1. 当随 XTYP_EXECUTE 事务传递的命令请求服务器终止时,则在从处理 XTYP_EXECUTE 返回回调函数之前,服务器不应终止。
  2. 如果服务器必须执行操作,例如处理可能会导致出现 DDEML 递归问题的对话框或 DDE 事务,服务器应返回 CBR_BLOCK 返回代码来阻止执行事务,执行操作,然后继续处理执行事务。

DdeClientTransaction 返回时,客户端可以使用 lpdwResult 参数访问事务状态标志。 如果标志是 DDE_FBUSY,客户端应稍后再次发送事务。

如果服务器不支持 XTYP_EXECUTE 事务,则应在 DdeInitialize 函数中指定 CBF_FAIL_EXECUTES 筛选器标志。 这样做,可防止 DDEML 将事务发送给服务器。

同步和异步事务

客户端可以发送同步或异步事务。 在同步事务中,客户端将指定一个超时值,该值会指示等待服务器处理事务的最长时间。 在服务器处理事务、事务失败或超时值过期之前,DdeClientTransaction 不会返回。 客户端会在调用 DdeClientTransaction 时指定超时值。

在同步事务期间,客户端会在等待处理事务时进入模式循环。 客户端仍可以处理用户输入,但在 DdeClientTransaction 返回之前无法发送另一个同步事务。

客户端可在 DdeClientTransaction 中指定 TIMEOUT_ASYNC 标志,以发送异步事务。 此函数会在事务开始后返回,从而将事务标识符传递给客户端。 服务器完成异步事务处理后,DDEML 会将 XTYP_XACT_COMPLETE 事务发送到客户端。 DDEML 在 XTYP_XACT_COMPLETE 事务期间传递给客户端的参数之一是事务标识符。 通过将此事务标识符与 DdeClientTransaction 返回的标识符进行比较,客户端会标识服务器已完成处理的异步事务。

客户端可以使用 DdeSetUserHandle 函数帮助处理异步事务。 此函数使客户端能够将应用程序定义的值与对话句柄和事务标识符相关联。 客户端可以在 XTYP_XACT_COMPLETE 事务期间使用 DdeQueryConvInfo 函数,以获取应用程序定义的值。 由于使用此函数,应用程序不需要维护活动事务标识符的列表。

当客户端使用同步事务成功完成数据请求时,DDEML 无法判断客户端何时使用收到的数据完成。 客户端应用程序必须将收到的数据句柄传递给 DdeFreeDataHandle 函数,从而通知 DDEML 不再使用该句柄。 同步事务返回的数据句柄实际上归客户端所有。

如果服务器没有及时处理异步事务,客户端可以调用 DdeAbandonTransaction 函数来放弃事务。 DDEML 释放与事务关联的所有资源,并在服务器处理完事务后放弃事务的结果。 同步事务期间的超时实际上会取消事务。

异步事务方法适用于必须发送大量 DDE 事务同时执行大量处理(例如执行计算)的应用程序。 异步方法也适用于必须暂时停止处理 DDE 事务的应用程序,以便它们可以在不中断的情况下完成其他任务。 在大多数其他情况下,应用程序应使用同步方法。

同步事务更易于维护,并且比异步事务快。 但是,一次只能执行一个同步事务,而可以同时执行许多异步事务。 使用同步事务,慢速服务器可能会导致客户端在等待响应时保持空闲状态。 此外,同步事务会导致客户端进入模式循环,该循环可能会绕过应用程序自己的消息循环中的消息筛选。

如果客户端已安装挂钩过程来筛选消息(也就是说,在调用 SetWindowsHookEx 函数时指定了 WH_MSGFILTER 挂钩类型),同步事务将不会导致系统绕过挂钩过程。 若在客户端正在等待同步事务结束时发生输入事件,挂钩过程会收到 MSGF_DDEMGR 挂钩代码。 使用同步事务模式循环的主要危险是对话框创建的模式循环可能会干扰其操作。 DLL 正在使用 DDEML 时,应始终使用异步事务。

事务控制

应用程序可以将事务挂起到其 DDE 回调函数,无论是与特定对话句柄关联的事务,还是所有事务,而不考虑对话句柄。 当应用程序收到需要长时间处理的事务时,此功能非常有用。 在这种情况下,应用程序可以返回 CBR_BLOCK 返回代码,以暂停与事务的对话句柄关联的将来事务,以便应用程序可以自由地处理其他对话。

处理完成后,应用程序会调用 DdeEnableCallback 函数,以恢复与挂起对话关联的事务。 调用 DdeEnableCallback 会导致 DDEML 重新发送导致应用程序挂起对话的事务。 因此,应用程序应以这样的方式存储事务的结果,这样就可以获取并返回结果,而无需重新处理事务。

应用程序可以在调用 DdeEnableCallback 时指定句柄和 EC_DISABLE 标志,以挂起与特定对话句柄关联的所有事务。 通过指定 NULL 句柄,应用程序可以挂起所有对话的所有事务。

当挂起对话时,DDEML 会将对话的事务保存到事务队列中。 当应用程序重新启用对话时,DDEML 将从队列中删除保存的事务,并将每个事务传递给相应的回调函数。 事务队列的容量很大,但应用程序应尽快重新启用挂起的对话,以避免丢失事务。

应用程序可以在 DdeEnableCallback 中指定 EC_ENABLEALL 标志,以恢复常规事务处理。 为了以更加可控的方式恢复事务处理,应用程序可以指定 EC_ENABLEONE 标志。 此标志会从事务队列中删除一个事务,并将其传递给相应的回调函数;处理该事务后,将再次禁用任何对话。

如果在调用 DdeEnableCallback 时指定了 EC_ENABLEONE 标志和对话句柄,则仅在处理事务后才会阻止该对话。 如果指定了 NULL 对话句柄,则会在任何对话中处理事务后阻止所有对话。

交易类

DDEML 有四类事务。 每个类均由以 XCLASS_ 前缀开头的常量标识。 这些类在 DDEML 标头文件中定义。 类值可与事务类型值相结合,并传递给接收应用程序的 DDE 回调函数。

事务的类确定回调函数在处理事务时应返回的返回值。 以下返回值和事务类型与四个事务类中的每一个相关联。

返回值 事务
XCLASS_BOOL TRUEFALSE XTYP_ADVSTART
XTYP_CONNECT
XCLASS_DATA 数据句柄、CBR_BLOCK 返回代码或 NULL XTYP_ADVREQ
XTYP_REQUEST
XTYP_WILDCONNECT
XCLASS_FLAGS 事务标志:DDE_FACK、DDE_FBUSY 或 DDE_FNOTPROCESSED XTYP_ADVDATA
XTYP_EXECUTE
XTYP_POKE
XCLASS_NOTIFICATION XTYP_ADVSTOP
XTYP_CONNECT_CONFIRM
XTYP_DISCONNECT
XTYP_ERROR
XTYP_REGISTER
XTYP_UNREGISTER
XTYP_XACT_COMPLETE

事务类型

每种 DDE 事务类型都有一个接收方和一个关联的活动,该活动会导致 DDEML 生成每种类型。

交易记录类型 接收方 原因
XTYP_ADVDATA 客户端 服务器通过返回数据句柄来响应 XTYP_ADVREQ 事务。
XTYP_ADVREQ 服务器 服务器调用了 DdePostAdvise 函数,从而指示建议循环中的数据项值已更改。
XTYP_ADVSTART 服务器 客户端在调用 DdeClientTransaction 函数时指定了 XTYP_ADVSTART 事务类型。
XTYP_ADVSTOP 服务器 客户端在调用 DdeClientTransaction 时指定了 XTYP_ADVSTOP 事务类型。
XTYP_CONNECT 服务器 客户端调用了 DdeConnect 函数,并指定了服务器支持的服务名称和主题名称。
XTYP_CONNECT_CONFIRM 服务器 服务器返回了 TRUE 来响应 XTYP_CONNECTXTYP_WILDCONNECT 事务。
XTYP_DISCONNECT 客户端/服务器 对话中的合作伙伴调用了 DdeDisconnect 函数,导致两个合作伙伴都收到此事务。
XTYP_ERROR 客户端/服务器 出现严重错误。 DDEML 可能没有足够的资源来继续。
XTYP_EXECUTE 服务器 客户端在调用 DdeClientTransaction 时指定了 XTYP_EXECUTE 事务类型。
XTYP_MONITOR DDE 监视应用程序 系统中发生了 DDE 事件。 有关 DDE 监视应用程序的详细信息,请参阅监视应用程序
XTYP_POKE 服务器 客户端在调用 DdeClientTransaction 时指定了 XTYP_POKE 事务类型。
XTYP_REGISTER 客户端/服务器 服务器应用程序使用 DdeNameService 函数注册了服务名称。
XTYP_REQUEST 服务器 客户端在调用 DdeClientTransaction 时指定了 XTYP_REQUEST 事务类型。
XTYP_UNREGISTER 客户端/服务器 服务器应用程序使用 DdeNameService 注销了服务名称。
XTYP_WILDCONNECT 服务器 客户端调用了 DdeConnectDdeConnectList 函数,从而为服务名称、主题名称或两者指定 NULL
XTYP_XACT_COMPLETE 客户端 客户端在调用 DdeClientTransaction 时指定 TIMEOUT_ASYNC 标志时发送的异步事务已结束。