客户端和服务器之间的会话始终在客户端请求时建立。 建立会话后,每个合作伙伴都会收到标识会话的句柄。 合作伙伴在其他动态数据交换管理库 (DDEML) 函数中使用此句柄来发送事务和管理聊天。 客户端可以请求与单个服务器的会话,也可以请求与一个或多个服务器的多个对话。
以下主题介绍了应用程序如何建立新对话并获取有关现有对话的信息。
单个对话
客户端应用程序通过调用 DdeConnect 函数并指定字符串句柄来请求与服务器的单个会话,这些句柄标识包含服务器应用程序的服务名称和会话的主题名称的字符串。 DDEML 通过向每个服务器应用程序的动态数据交换(DDE)回调函数发送 XTYP_CONNECT 事务来响应,该函数注册的服务名称与 DdeConnect 中指定的服务名称匹配,或者通过调用 DdeNameService来关闭服务名称筛选。 服务器还可以通过在 DdeInitialize 函数中指定CBF_FAIL_CONNECTIONS筛选器标志来筛选 XTYP_CONNECT 事务。 在 XTYP_CONNECT 事务期间,DDEML 会将服务名称和主题名称传递给服务器。 如果服务器支持服务名称和主题名称对,则服务器必须检查名称并返回 TRUE;否则 FALSE。
如果没有服务器对客户端的连接请求做出积极响应,则客户端从 DdeConnect 接收 NULL,并且未建立任何会话。 如果服务器返回 TRUE,则会建立会话,并且客户端会收到会话句柄,即标识会话的 DWORD 值。 客户端在后续 DDEML 调用中使用句柄从服务器获取数据。 服务器接收 XTYP_CONNECT_CONFIRM 事务(除非服务器指定了CBF_SKIP_CONNECT_CONFIRMS筛选器标志)。 此事务将会话句柄传递给服务器。
以下示例使用可识别服务名称 MyServer 的服务器请求系统主题上的对话。 hszServName 和 hszSysTopic 参数以前是创建字符串句柄。
HCONV hConv; // conversation handle
HWND hwndParent; // parent window handle
HSZ hszServName; // service name string handle
HSZ hszSysTopic; // System topic string handle
hConv = DdeConnect(
idInst, // instance identifier
hszServName, // service name string handle
hszSysTopic, // System topic string handle
(PCONVCONTEXT) NULL); // use default context
if (hConv == NULL)
{
MessageBox(hwndParent, "MyServer is unavailable.",
(LPSTR) NULL, MB_OK);
return FALSE;
}
在前面的示例中,DdeConnect 会导致 MyServer 应用程序的 DDE 回调函数接收 XTYP_CONNECT 事务。
在以下示例中,服务器通过将传递给服务器的 DDEML 与服务器名称字符串数组中的每个元素进行比较来响应 XTYP_CONNECT 事务。 如果服务器找到匹配项,则会建立对话。
#define CTOPICS 5
HSZ hsz1; // string handle passed by DDEML
HSZ ahszTopics[CTOPICS]; // array of supported topics
int i; // loop counter
// Use a switch statement to examine transaction types.
// Here is the connect case.
case XTYP_CONNECT:
for (i = 0; i < CTOPICS; i++)
{
if (hsz1 == ahszTopics[i])
return TRUE; // establish a conversation
}
return FALSE; // Topic not supported; deny conversation.
// Process other transaction types.
如果服务器返回 TRUE 以响应 XTYP_CONNECT 事务,则 DDEML 会将 XTYP_CONNECT_CONFIRM 事务发送到服务器的 DDE 回调函数。 服务器可以通过处理此事务来获取会话的句柄。
客户端可以通过为服务名称字符串句柄、主题名称字符串句柄或调用 DdeConnect指定 NULL 来建立通配符会话。 如果至少有一个字符串句柄 NULL,则 DDEML 会将 XTYP_WILDCONNECT 事务发送到所有 DDE 应用程序的回调函数(筛选 XTYP_WILDCONNECT 事务除外)。 每个服务器应用程序都应通过返回一个数据句柄来响应,该句柄标识 HSZPAIR 结构的 null 终止数组。 如果服务器应用程序尚未调用 DdeNameService 注册其服务名称,并且筛选处于打开的状态,则服务器不会收到 XTYP_WILDCONNECT 事务。 有关数据句柄的详细信息,请参阅 数据管理。
该数组必须包含与客户端指定的对匹配的每个服务名称和主题名称对的一个结构。 DDEML 选择其中一对来建立会话,并返回到客户端一个标识会话的句柄。 DDEML 将 XTYP_CONNECT_CONFIRM 事务发送到服务器(除非服务器筛选此事务)。 以下示例显示了对 XTYP_WILDCONNECT 事务的典型服务器响应。
#define CTOPICS 2
UINT uType;
HSZPAIR ahszp[(CTOPICS + 1)];
HSZ ahszTopicList[CTOPICS];
HSZ hszServ, hszTopic;
WORD i, j;
if (uType == XTYP_WILDCONNECT)
{
// Scan the topic list and create an array of HSZPAIR structures.
j = 0;
for (i = 0; i < CTOPICS; i++)
{
if (hszTopic == (HSZ) NULL ||
hszTopic == ahszTopicList[i])
{
ahszp[j].hszSvc = hszServ;
ahszp[j++].hszTopic = ahszTopicList[i];
}
}
// End the list with an HSZPAIR structure that contains NULL
// string handles as its members.
ahszp[j].hszSvc = NULL;
ahszp[j++].hszTopic = NULL;
// Return a handle to a global memory object containing the
// HSZPAIR structures.
return DdeCreateDataHandle(
idInst, // instance identifier
(LPBYTE) &ahszp, // pointer to HSZPAIR array
sizeof(HSZ) * j, // length of the array
0, // start at the beginning
(HSZ) NULL, // no item name string
0, // return the same format
0); // let the system own it
}
客户端或服务器都可以通过调用 DdeDisconnect 函数随时终止会话。 此函数导致会话中伙伴的回调函数接收 XTYP_DISCONNECT 事务(除非合作伙伴指定了CBF_SKIP_DISCONNECTS筛选器标志)。 通常,应用程序使用 DdeQueryConvInfo 函数来响应 XTYP_DISCONNECT 事务,以获取有关终止会话的信息。 回调函数从处理 XTYP_DISCONNECT 事务返回后,会话句柄将不再有效。
在其 DDE 回调函数中接收 XTYP_DISCONNECT 事务的客户端应用程序可以通过调用 DdeReconnect 函数来尝试重新建立会话。 客户端必须从其 DDE 回调函数中调用 DdeReconnect。
多个对话
客户端应用程序可以使用 DdeConnectList 函数来确定系统中是否有任何感兴趣的服务器。 客户端在调用 DdeConnectList时指定服务名称和主题名称,导致 DDEML 将 XTYP_WILDCONNECT 事务广播到与服务名称匹配的所有服务器的 DDE 回调函数(筛选事务除外)。 服务器的回调函数应返回一个数据句柄,该句柄标识 HSZPAIR 结构的以 null 结尾的数组。 该数组应包含与客户端指定的对匹配的每个服务名称和主题名称对的一个结构。 DDEML 为服务器填充的每个 HSZPAIR 结构建立会话,并向客户端返回会话列表句柄。 服务器通过 XTYP_CONNECT 事务接收会话句柄(除非服务器筛选此事务)。
当客户端调用 DdeConnectList时,客户端可以为服务名称、主题名称指定 NULL。 如果服务名称 NULL,则支持指定主题名称的系统中的所有服务器都会响应。 与每个响应服务器建立对话,包括同一服务器的多个实例。 如果主题名称 NULL,则会在每个与服务名称匹配的服务器识别的每个主题上建立对话。
客户端可以使用 DdeQueryNextServer 和 DdeQueryConvInfo 函数来标识响应 DdeConnectList的服务器。 DdeQueryNextServer 返回会话列表中的下一个会话句柄,DdeQueryConvInfo 填充 CONVINFO 结构,其中包含有关会话的信息。 客户端可以保留它所需的会话句柄,并从对话列表中放弃其余的句柄。
以下示例使用 DdeConnectList 与支持系统主题的所有服务器建立对话,然后使用 DdeQueryNextServer 和 DdeQueryConvInfo 函数获取服务器的服务名称字符串句柄并将其存储在缓冲区中。
HCONVLIST hconvList; // conversation list
DWORD idInst; // instance identifier
HSZ hszSystem; // System topic
HCONV hconv = NULL; // conversation handle
CONVINFO ci; // holds conversation data
UINT cConv = 0; // count of conv. handles
HSZ *pHsz, *aHsz; // point to string handles
// Connect to all servers that support the System topic.
hconvList = DdeConnectList(idInst, NULL, hszSystem, NULL, NULL);
// Count the number of handles in the conversation list.
while ((hconv = DdeQueryNextServer(hconvList, hconv)) != NULL)
cConv++;
// Allocate a buffer for the string handles.
hconv = NULL;
aHsz = (HSZ *) LocalAlloc(LMEM_FIXED, cConv * sizeof(HSZ));
// Copy the string handles to the buffer.
pHsz = aHsz;
while ((hconv = DdeQueryNextServer(hconvList, hconv)) != NULL)
{
DdeQueryConvInfo(hconv, QID_SYNC, (PCONVINFO) &ci);
DdeKeepStringHandle(idInst, ci.hszSvcPartner);
*pHsz++ = ci.hszSvcPartner;
}
// Use the handles; converse with the servers.
// Free the memory and terminate the conversations.
LocalFree((HANDLE) aHsz);
DdeDisconnectList(hconvList);
应用程序可以通过调用 DdeDisconnect 函数来终止会话列表中的单个会话。 应用程序可以通过调用 DdeDisconnectList 函数来终止会话列表中的所有会话。 这两个函数都会导致 DDEML 向每个伙伴的 DDE 回调函数发送 XTYP_DISCONNECT 事务。 DdeDisconnectList 为列表中的每个会话句柄发送 XTYP_DISCONNECT 事务。
客户端可以通过将现有会话列表句柄传递给 DdeConnectList来检索会话列表中的会话句柄列表。 枚举过程从列表中删除终止的对话的句柄,并添加适合指定服务名称和主题名称的非重复会话。
如果 DdeConnectList 指定现有会话列表句柄,该函数将创建一个新的会话列表,其中包含任何新会话的句柄以及现有列表中的句柄。
如果存在重复对话,DdeConnectList 尝试阻止会话列表中的重复会话句柄。 重复会话是同一服务名称和主题名称上具有相同服务器的第二个会话。 两个这样的对话将有不同的句柄,但他们将识别相同的对话。