Conversation Management
クライアントとサーバーの間の会話は、クライアントの要求時に常に確立されます。 会話が確立されると、各パートナーは会話を識別するハンドルを受け取ります。 パートナーは、他の動的データ交換管理ライブラリ (DDEML) 関数でこのハンドルを使用して、トランザクションを送信し、会話を管理します。 クライアントは、1 つのサーバーとの会話を要求することも、1 つ以上のサーバーとの複数の会話を要求することもできます。
次のトピックでは、アプリケーションが新しい会話を確立し、既存の会話に関する情報を取得する方法について説明します。
シングルカンバセーション
クライアント アプリケーションは、 DdeConnect 関数を呼び出し、サーバー アプリケーションのサービス名と会話のトピック名を含む文字列を識別する文字列ハンドルを指定することで、サーバーとの 1 つの会話を要求します。 DDEML は、 DdeConnect で指定されたサービス名に一致するサービス名を登録しているか、 DdeNameService を呼び出してサービス名のフィルター処理をオフにした各サーバー アプリケーションの動的データ 交換 (DDE) コールバック関数に、 XTYP_CONNECT トランザクションを送信することによって応答します。 サーバーは、 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.
サーバーが XTYP_CONNECT トランザクションに応答して TRUE を返す場合、 DDEML はサーバーの DDE コールバック関数に XTYP_CONNECT_CONFIRM トランザクションを送信します。 サーバーは、このトランザクションを処理することで、会話へのハンドルを取得できます。
クライアントは、 DdeConnect の呼び出しでサービス名文字列ハンドル、トピック名文字列ハンドル、またはその両方に NULL を指定することで、ワイルドカード会話を確立できます。 文字列ハンドルの少なくとも 1 つが NULL の場合、DDEML は XTYP_WILDCONNECT トランザクションをすべての DDE アプリケーションのコールバック関数に送信します (XTYP_WILDCONNECT トランザクションをフィルター処理するものを除く)。 各サーバー アプリケーションは、 HSZPAIR 構造体の null で終わる配列を識別するデータ ハンドルを返すことによって応答する必要があります。 サーバー アプリケーションが DdeNameService を呼び出してサービス名を登録していない場合、およびフィルター処理がオンになっている場合、サーバーは XTYP_WILDCONNECT トランザクションを受け取りません。 データ・ハンドルの詳細は、 データ管理 を参照してください。
配列には、クライアントによって指定されたペアと一致するサービス名とトピック名のペアごとに 1 つの構造体が含まれている必要があります。 DDEML は、会話を確立するペアの 1 つを選択し、会話を識別するハンドルをクライアントに返します。 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 は、サービス名に一致するすべてのサーバーの DDE コールバック関数 (トランザクションをフィルター処理するサーバーを除く) に XTYP_WILDCONNECT トランザクションをブロードキャストします。 サーバーのコールバック関数は、 HSZPAIR 構造体の null で終わる配列を識別するデータ ハンドルを返す必要があります。 配列には、クライアントによって指定されたペアと一致するサービス名とトピック名のペアごとに 1 つの構造体が含まれている必要があります。 DDEML は、サーバーによって満たされた HSZPAIR 構造体ごとに会話を確立し、メッセージ交換リスト ハンドルをクライアントに返します。 サーバーは、 XTYP_CONNECT トランザクションを使用して会話ハンドルを受け取ります (サーバーがこのトランザクションをフィルター処理する場合を除く)。
クライアントは、 DdeConnectList を呼び出すときに、サービス名、トピック名、またはその両方に NULL を指定できます。 サービス名が NULL の場合、指定されたトピック名をサポートするシステム内のすべてのサーバーが応答します。 同じサーバーの複数のインスタンスを含む、応答する各サーバーとの会話が確立されます。 トピック名が NULL の場合、サービス名と一致する各サーバーによって認識される各トピックで会話が確立されます。
クライアントは、 DdeQueryNextServer 関数と DdeQueryConvInfo 関数を使用して、 DdeConnectList に応答するサーバーを識別できます。 DdeQueryNextServer は会話リスト内の次の会話ハンドルを返し、DdeQueryConvInfo は会話に関する情報を CONVINFO 構造体に格納します。 クライアントは、必要な会話ハンドルを保持しカード残りを会話リストから削除できます。
次の例では、 DdeConnectList を使用して、System トピックをサポートするすべてのサーバーとの会話を確立し、 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 は会話リスト内の会話ハンドルの重複を防止しようとします。 重複する会話は、同じサービス名とトピック名で同じサーバーとの 2 つ目の会話です。 このような 2 つの会話は異なるハンドルを持ちますが、同じ会話を識別します。