レガシー Windows API(概要)について

ここで紹介しているAPIは、Windows95、Windows NT3.5時代のAPI群であり、最新のAPIは含まれていないことに注しして欲しい。なお、当時の解説書は現在よりもより詳細に解説しているため、参考になるはずである。

  • レイアウトを変換する際、崩れているページがあります。
  • 概要およびSampleコードはありますが、APIの解説はありません。

ご要望があれば旧版のWin32 API(HELP形式)をお譲りします。問い合わせフォームより申し込みください。

Windows API 動的データ交換管理ライブラリの概要(DDE)

次に示すトピックでは、 DDEの概念と、 DDE管理ライブラリを使ってアプリケーションにDDE機能を追加する方法を説明します。

DDEMLについて

クライアントとサーバーの対話

トランザクションとDDEコールバック関数

サービス名、 トピック名、 項目名

Systemトピック

DDEMLとスレッド

初期化

コールバック関数

文字列の管理

名前サービス

サービス名の登録

サービス名フィルタ

対話の管理

単一の対話

複数の対話

データの管理

トランザクションの管理

要求トランザクション

ポーク トランザクション

アドバイズ トランザクション

実行トランザクション

同期トランザクションと非同期トランザクション

トランザクションの制御

トランザクションのクラス

トランザクションの要約

エラーの検出

監視アプリケーション

動的データ交換管理関数

Microsoft(R) Windows(TM) の「動的データ交換」 (DDE)は、 共有メモリによってアプリケーション間でデータを交換する、 一種のプロセス間通信です。DDEは、 1回だけのデータ転送や、 新しいデータが利用可能になると更新データを送るアプリケーション間での継続的なデータ交換で使われます。

DDEは、 Windowsオペレーティング システムのクリップボード データ転送機構とは異なります。通常、 クリップボードは、 ユーザーの特定の動作 (メニューからの[貼り付け]コマンドの選択など) に対する1回限りの応答として使われます。DDEは、 ユーザーが開始することもできますが、 通常は、 ユーザーから見えないところで処理が続行されます。

DDEMLは、 WindowsアプリケーションにDDE機能を簡単に付加できるようにするためのアプリケーション プログラミング インターフェイスを提供しています。DDEML関数を使うことによって、 DDEメッセージの送信、 ポスト、 処理などを直接行わずに、 DDE対話を管理できます。「DDE対話」とは、 クライアント アプリケーションとサーバー アプリケーションとの対話です。また、 DDEMLは、 DDEアプリケーション間で共有されている文字列やデータを管理する機能も提供しています。DDEアプリケーションは、 共有メモリを指すポインタやアトムを使わずに、 文字列ハンドルやデータ ハンドルを作成して交換します。文字列ハンドルは文字列を識別子、 データ ハンドルはメモリ オブジェクトを識別します。さらに、 DDEMLによって、 サーバー アプリケーションは自分がサポートするサービス名を登録できます。サービス名はシステム内のほかのアプリケーションに通知され、 ほかのアプリケーションはサービス名を使ってサーバーに接続できます。そして、 DDEMLによってDDEプロトコルが一貫した方法で実現されるため、 DDEアプリケーション間の互換性が保証されます。

メッセージ ベースのDDEプロトコルを使用している既存のアプリケーションは、 DDEMLを使用するアプリケーションと完全な互換性があります。つまり、 メッセージ ベースのプロトコルを使用しているアプリケーションは、 DDEMLを使用しているアプリケーションと対話を確立したり、 トランザクションを実行できます。DDEMLには多くの利点があるため、 新しいアプリケーションではDDEメッセージではなくDDEMLを使ってください。

DDEMLライブラリのAPIを使うには、 ソース ファイルでDDEML.Hヘッダー ファイルをインクルードしてUSER32.LIBとリンクし、 DDEML.DLLをシステムのパスに置いてください。

基本概念

ここで説明される基本概念はDDEDDEMLを理解するためのキーとなります。

クライアントとサーバーの対話

DDEは、 常に、 クライアント アプリケーションとサーバー アプリケーションとの間で発生します。「DDEクライアント アプリケーション」は、 サーバーにトランザクションを送るためのサーバーとの対話を確立することによって、 データ交換を開始します。トランザクションとは、 データやサービスの要求です。「DDEサーバー アプリケーション」は、 データやサービスをクライアントに提供することによって、 トランザクションに応答します。たとえば、 企業の四半期の利益を示す棒グラフがグラフィック アプリケーションにあり、 棒グラフのデータが表計算アプリケーションにあるとします。グラフィック アプリケーション (クライアント) は、 最新の利益データを取得するため、 表計算アプリケーション (サーバー) との対話を確立します。それから、 グラフィック アプリケーションは、 最新の利益のデータを要求するトランザクションを表計算アプリケーションに送ります。

サーバーは同時に複数のクライアントを持つことができ、 クライアントは複数のサーバーにデータを要求できます。また、 アプリケーションは、 クライアントとサーバーの両方になることができます。対話は、 クライアントとサーバーのどちらからでもいつでも終了できます。

トランザクションとDDEコールバック関数

DDEMLは、 アプリケーションのDDEコールバック関数にトランザクションを送ることによって、 アプリケーションに関するDDE動作をアプリケーションに通知します。「DDEトランザクション」はメッセージのようなもので、 トランザクションに関する追加情報を示すほかのパラメータが付加された名前付きの定数です。

DDEMLは、 トランザクションの種類に適した動作を実行するアプリケーション定義のDDEコールバック関数にトランザクションを渡します。たとえば、 クライアント アプリケーションは、 サーバーとの対話を確立しようとするとき、 DdeConnect関数を呼び出します。これによって、 DDEMLは、 XTYP_CONNECTトランザクションをサーバーのDDEコールバック関数に送ります。コールバック関数は、 TRUEDDEMLに返して対話を許可するか、 FALSEを返して対話を拒否することができます。

トランザクションについて詳しくは、 トランザクションの管理を参照してください。

サービス名、 トピック名、 項目名

DDEサーバーは、 サービス名 (以前のDDEのマニュアルでは「アプリケーション名」と呼ばれていました)、 トピック名、 項目名の3段階の階層構造を使って、 対話で交換される1単位のデータを一意に識別します。

「サービス名」とは、 クライアントがサーバーとの対話を確立しようとしたときにサーバー アプリケーションが応答する文字列です。サーバーとの対話を確立するには、 クライアントはこのサービス名を指定しなければなりません。サーバーは複数のサービス名に応答できますが、 通常は1つのサービス名だけに応答します。

「トピック名」とは、 論理データ コンテキストを識別する文字列です。通常、 ファイル ベースのドキュメントを操作するアプリケーションでは、 トピックはファイル名です。そのほかのアプリケーションでは、 トピックはアプリケーション固有の名前です。クライアントは、 サーバーとの対話を確立しようとするとき、 サーバーのサービス名とともにトピック名を指定しなければなりません。

「項目名」とは、 サーバーがトランザクション中にクライアントに渡す1単位のデータを識別する文字列です。たとえば、 項目名は、 整数や文字列、 数段落のテキスト、 ビットマップなどを識別します。

クライアントは、 これらの名前を使って、 サーバーとの対話を確立し、 データを要求できます。

Systemトピック

Systemトピックは、 すべてのDDEクライアントが関係する情報のコンテキストを提供します。サーバー アプリケーションは、 Systemトピックを必ずサポートしてください。Systemトピックは、 DDEMLヘッダー ファイルでSZDDESYS_TOPICとして定義されています。

サーバーが存在するかどうかやサーバーが提供する情報の種類を判断するには、 クライアント アプリケーションは、 起動時に、 デバイス名にNULLを設定してSystemトピックでの対話を要求します。このようなワイルドカード対話はシステムの性能に大きな影響を与えるため、 使用は最小限にしてください。

DDE対話の開始について詳しくは、 対話の管理を参照してください。

サーバーは、 Systemトピックで、 次に示す項目名をサポートしなければなりません。また、 そのほかにもクライアントに有用な項目名もサポートしてください。

項目 説明

SZDDE_ITEM_ITEMLISTSystemトピック以外のトピックでサポートされる項目のリストです (このリストは動的に変更される可能性があり、 トピックによって異なります)

SZDDESYS_ITEM_FORMATS サーバー アプリケーションがサポート可能なすべてのクリップボード形式を示す文字列をタブで区切ったリストです。定義済みのクリップボード形式を示す文字列は、 CF_定数名からCF_プリフィックスを除いた文字列です。たとえば、 CF_TEXT形式は、 TEXTという文字列で表されます。定義済みの形式として識別するため、 これらの文字列は大文字でなければなりません。形式のリストは、 内容が豊富なものから順に並べなければなりません。クリップボード形式とデータの表現について詳しくは、 クリップボードの概要を参照してください。

SZDDESYS_ITEM_HELP ユーザーが理解できる形式の、 一般的な情報です。この情報には、 少なくとも、 サーバー アプリケーションのDDE機能の使い方が含まれていなければなりません。そのほかにも、 トピック内の項目の指定方法、 サーバーが実行可能な実行文字列、 ポーク トランザクションが可能かどうか、 ほかのSystemトピックに関するヘルプの取得方法などを含めることもできます。

SZDDESYS_ITEM_RTNMSG 最近使われたWM_DDE_ACKメッセージの詳細を示します。これは、 8ビットを超えるアプリケーション固有の戻りデータが必要なときに役立ちます。

SZDDESYS_ITEM_STATUS サーバーの現在の状態を示します。通常、 この項目はCF_TEXT形式だけをサポートし、 ReadyまたはBusyを示します。

SZDDESYS_ITEM_SYSITEMS このサーバーがSystemトピックでサポートする項目のリストです。

SZDDESYS_ITEM_TOPICS サーバーが現在サポートしているトピックのリストです (このリストは動的に変更される可能性があります)

上記の項目名は、 DDEMLのヘッダー ファイルで定義されている値です。これらの文字列の文字列ハンドルを取得するには、 ほかのDDEML文字列と同様に、 DDEML文字列管理関数を使ってください。文字列の管理について詳しくは、 文字列の管理を参照してください。

初期化

アプリケーションは、 なんらかのDDEML関数を呼び出す前に、 まずDdeInitialize関数を呼び出さなければなりません。アプリケーションのインスタンス識別子と、 コールバック関数のアドレスおよびトランザクション フィルタを指定すると、 DdeInitializeは、 アプリケーションのDDEコールバック関数をDDEMLに登録します。

アプリケーションやダイナミック リンク ライブラリ (DLL) の各インスタンスは、 インスタンス識別子を必要とするDDEML関数のidInstパラメータに自分のインスタンス識別子を渡さなければなりません。DDEMLのインスタンスが複数存在するのは、 アプリケーションがDDEMLを使用しているときに同時にDDEMLを使用するDLLをサポートするためです。アプリケーションは、 複数のDDEMLのインスタンスを使ってはいけません。

「トランザクション フィルタ」によって、 DDEMLはアプリケーションのDDEコールバック関数に不要なトランザクションを渡さなくなるため、 システムの性能が改善されます。トランザクション フィルタは、 DdeInitializeを呼び出すときに設定します。コールバック関数で処理しないトランザクションの種類に対しては、 トランザクション フィルタ フラグをセットしてください。トランザクション フィルタは、 後でDdeInitializeを呼び出して変更することもできます。

トランザクションについて詳しくは、 トランザクションの管理を参照してください。

DDEMLを使うようにアプリケーションを初期化する方法を次の例に示します。

DWORD idInst = 0;
HINSTANCE hinst;

.
.
.
DdeInitialize(&idInst,/* receives instance identifier */
(PFNCALLBACK) DdeCallback, /* address of callback function */
CBF_FAIL_EXECUTES |/* filter XTYPE_EXECUTE*/
CBF_SKIP_ALLNOTIFICATIONS, /* filter notifications*/
0);
.
.
.

DDEMLが不要になったら、 DdeUninitialize関数を呼び出してください。この関数は、 アプリケーションで現在オープンされている対話をすべて終了し、 システムがアプリケーションのために割り当てたDDEMLリソースを解放します。

コールバック関数

DDEMLを使用するアプリケーションは、 アプリケーションに関するDDEイベントを処理するコールバック関数を提供しなければなりません。DDEMLは、 アプリケーションのDDEコールバック関数にトランザクションを送ることによってDDEイベントを通知します。コールバック関数が受け取るトランザクションは、 アプリケーションがDdeInitializeで指定したコールバック フィルタ フラグや、 アプリケーションがサーバーかクライアントか、 またはその両方かによって異なります。次の例は、 典型的なクライアント アプリケーションのコールバック関数の全体構造を示しています。

HDDEDATA CALLBACK DdeCallback(uType, uFmt, hconv, hsz1,
hsz2, hdata, dwData1, dwData2)
UINT uType;/* transaction type*/
UINT uFmt;/* clipboard data format */
HCONV hconv;/* handle of conversation */
HSZ hsz1;/* handle of string*/
HSZ hsz2;/* handle of string*/
HDDEDATA hdata;/* handle of global memory object*/
DWORD dwData1;/* transaction-specific data*/
DWORD dwData2; /* transaction-specific data*/
{
switch (uType) {
case XTYP_REGISTER:
case XTYP_UNREGISTER:
.
.
.
return (HDDEDATA) NULL;

case XTYP_ADVDATA:
.
.
.
return (HDDEDATA) DDE_FACK;

case XTYP_XACT_COMPLETE:
.
.
.
return (HDDEDATA) NULL;

case XTYP_DISCONNECT:
.
.
.
return (HDDEDATA) NULL;

default:
return (HDDEDATA) NULL;
}
}

uTypeパラメータは、 DDEMLがコールバック関数に送ったトランザクションの種類を示します。そのほかのパラメータの意味は、 トランザクションの種類によって異なります。各トランザクションの種類について詳しくは、 トランザクションの管理を参照してください。

文字列の管理

多くのDDEML関数は、 DDE処理を実行するために文字列へのアクセスを必要とします。たとえば、 クライアントは、 DdeConnect関数を呼び出してサーバーとの対話を要求するとき、 サービス名とトピック名を指定しなければなりません。DDEML関数には、 ポインタではなく文字列ハンドルを使って文字列を指定します。「文字列ハンドル」とは、 文字列を識別するダブルワード値で、 システムが割り当てます。

特定の文字列の文字列ハンドルを取得するには、 DdeCreateStringHandle関数を呼び出します。この関数は、 文字列をシステムに登録し、 文字列ハンドルをアプリケーションに返します。アプリケーションは、 文字列を必要とするDDEML関数にこの文字列ハンドルを渡すことができます。次の例は、 Systemトピック文字列とサービス名文字列の文字列ハンドルを取得します。

HSZ hszServName;
HSZ hszSysTopic;
.
.
.
hszServName = DdeCreateStringHandle(
idInst,/* instance identifier*/
"MyServer",/* string to register*/
CP_WINANSI);/* Windows ANSI code page */

hszSysTopic = DdeCreateStringHandle(
idInst,/* instance identifier*/
SZDDESYS_TOPIC, /* System topic*/
CP_WINANSI);/* Windows ANSI code page */
.
.
.

上記の例のidInstパラメータには、 DdeInitialize関数で取得したインスタンス識別子を指定します。

通常、 アプリケーションのコールバック関数は、 DDE対話中に文字列ハンドルを受け取ります。たとえば、 サーバーは、 XTYP_REQUESTトランザクションで2つの文字列ハンドルを受け取ります。1つはトピック名を示す文字列を識別し、 もう1つは項目名を示す文字列を識別します。文字列ハンドルに対応する文字列の長さを取得し、 アプリケーションが供給するバッファに文字列をコピーするには、 次の例のようにDdeQueryString関数を呼び出します。

DWORD idInst;
DWORD cb;
HSZ hszServ;
PSTR pszServName;
.
.
.
cb = DdeQueryString(idInst, hszServ, (LPSTR) NULL, 0,
CP_WINANSI) + 1;
pszServName = (PSTR) LocalAlloc(LPTR, (WORD) cb);
DdeQueryString(idInst, hszServ, pszServName, cb, CP_WINANSI);
.
.
.

インスタンス固有の文字列ハンドルは、 文字列ハンドルから文字列にマップしてもう一度文字列ハンドルにマップしなおすことはできません。たとえば、 次の例では、 DdeQueryStringで文字列ハンドルから文字列を作成し、 DdeCreateStringHandleでその文字列から文字列を作成していますが、 2つのハンドルは同じではありません。

DWORD idInst;
DWORD cb;
HSZ hszInst, hszNew;
PSZ pszInst;
.
.
.
DdeQueryString(idInst, hszInst, pszInst, cb, CP_WINANSI);
hszNew = DdeCreateStringHandle(idInst, pszInst, CP_WINANSI);
/* hszNew != hszInst ! */
.
.
.

アプリケーションのDDEコールバック関数に渡された文字列ハンドルは、 コールバック関数が戻ると無効になります。コールバック関数が戻った後で使うために文字列ハンドルを保存するには、 DdeKeepStringHandle関数を使います。

アプリケーションがDdeCreateStringHandleを呼び出すと、 システムは、 指定された文字列を文字列テーブルに置いて、 その文字列をアクセスするのに使われるハンドルを作成します。また、 システムは、 文字列テーブル内の各文字列に対して使用カウントを管理しています。

すでにテーブルに存在する文字列を指定してDdeCreateStringHandleを呼び出すと、 システムは、 文字列をもう1つ追加せずに、 使用カウントを1つ増やします (DdeKeepStringHandleを使ったときも使用カウントが1つ増やされます)。アプリケーションがDdeFreeStringHandle関数を呼び出すと、 システムは使用カウントを1つ減らします。

使用カウントが0になると、 文字列はテーブルから削除されます。特定の文字列のハンドルを複数のアプリケーションが取得している可能性があるため、 アプリケーションは、 自分が作成したり保存した回数以上に文字列ハンドルを解放しないでください。そうでないと、 文字列がテーブルから削除されてしまい、 ほかのアプリケーションが文字列にアクセスできなくなります。

DDEML文字列管理関数はWindowsのアトム マネージャに基づいており、 アトムと同じサイズ制限が適用されます。

名前サービス

DDEMLでは、 サーバー アプリケーションが自分のサポートするサービス名を登録することによって、 サーバーのDDEコールバック関数がサポートしていないサービス名のXTYP_CONNECTトランザクションが送られないようにしています。

サービス名の登録

サーバーは、 自分のサービス名をDDEMLに登録することによって、 新しいサーバーが利用可能になったことをシステム内のほかのDDEアプリケーションに通知できます。サーバーは、 サービス名を識別する文字列ハンドルを指定してDdeNameService関数を呼び出すことによってサービス名を登録します。この呼び出しに対して、 DDEMLは、 システム内の各DDEMLアプリケーションの (DdeInitialize関数でCBF_SKIP_REGISTRATIONSフィルタ フラグを指定したものを除く) コールバック関数にXTYP_REGISTERトランザクションを送ります。XTYP_REGISTERトランザクションでは、 コールバック関数に2つの文字列ハンドルが渡されます。1つ目のハンドルは、 基本サービス名を示す文字列を識別します。2つ目のハンドルは、 インスタンス固有サービスを示す文字列を識別します。通常、 クライアントは、 利用可能サーバーのリストの基本サービス名を使って、 ユーザーがリストからサーバーを選択できるようにします。クライアントは、 複数のサーバー インスタンスが動作しているときに、 特定のサーバーとの対話を確立するためにインスタンス固有サービスを使います。

サーバーは、 DdeNameServiceを使ってサービス名を登録解除できます。これによって、 DDEMLは、 システム内のほかのDDEアプリケーションにXTYP_UNREGISTERトランザクションを送り、 そのサービス名を使って対話を確立できなくなったことを通知します。

サーバーは、 DdeInitializeを呼び出した後すぐに、 DdeNameServiceを呼び出して自分のサービス名を登録しなければなりません。また、 サーバーは、 DdeUninitialize関数を呼び出す直前に、 DdeNameServiceを使って自分のサービス名を登録解除しなければなりません。

サービス名フィルタ

DdeNameServiceは、 サービス名を登録するほかに、 サービス名フィルタをオンまたはオフにします。サーバーがサービス名フィルタをオフにすると、 クライアントがDdeConnect関数を呼び出すたびに、 指定されているサービス名にかかわらず、 DDEMLXTYP_CONNECTトランザクションをサーバーのDDEコールバック関数に送ります。サーバーがサービス名フィルタをオンにすると、 DDEMLは、 サーバーがDdeNameServiceで指定したサービス名がDdeConnectで指定されたときだけ、 XTYP_CONNECTトランザクションをサーバーに送ります。

デフォルトでは、 アプリケーションがDdeInitializeを呼び出したときにはサービス名フィルタはオンになっています。このため、 必要な文字列ハンドルをサーバーが作成するまでは、 DDEMLXTYP_CONNECTトランザクションをサーバーに送りません。サーバーは、 DNS_FILTEROFFフラグを指定してDdeNameServiceを呼び出すことによってサービス名フィルタをオフにできます。DNS_FILTERONフラグはフィルタをオンにします。

対話の管理

クライアントとサーバーの対話は、 常にクライアントの要求によって確立されます。対話が確立されると、 各パートナーは、 対話を識別するハンドルを受け取ります。パートナーは、 トランザクションを送ったり対話を管理するためにDDEML関数でこのハンドルを使います。クライアントは、 単一のサーバーに単一の対話を要求したり、 1つ以上のサーバーに複数の対話を要求できます。

この節の残りのトピックでは、 アプリケーションが新しく対話を確立する方法、 および既存の対話についての情報を取得する方法について述べます。

単一の対話

サーバーに対して単一の対話を要求するには、 クライアントは、 サーバー アプリケーションのサービス名と対話トピック名を示す文字列を識別する文字列ハンドルを指定して、 DdeConnect関数を呼び出します。DDEMLは、 DdeConnectで指定されたサービス名と同じサービス名を登録している各サーバー アプリケーションやDdeNameServiceを呼び出してサービス名フィルタをオフにしているサーバー アプリケーションののDDEコールバック関数にXTYP_CONNECTトランザクションを送ります。また、 サーバーは、 DdeInitialize関数を呼び出すときにCBF_FAIL_CONNECTIONSフィルタ フラグを指定することによって、 XTYP_CONNECTトランザクションをフィルタできます。XTYP_CONNECTトランザクションでは、 DDEMLは、 サービス名とトピック名をサーバーに渡します。サーバーは、 渡された名前を調べて、 そのサービス名とトピック名のペアをサポートするときはTRUEを、 サポートしないときはFALSEを返さなければなりません。

クライアントの接続要求に肯定の応答を返すサーバーがなければ、 クライアントはDdeConnectからNULLを受け取り、 対話は確立されません。サーバーがTRUEを返すと、 対話は確立され、 クライアントは対話ハンドル (対話を識別するダブルワード値) を受け取ります。クライアントは、 DDEML関数でこのハンドルを使って、 サーバーからデータを取得します。サーバーは、 (CBF_FAIL_CONFIRMSフラグを指定していなければ)XTYP_CONNECT_CONFIRMトランザクションを受け取ります。このトランザクションは、 サーバーに対話ハンドルを渡します。

次の例は、 サービス名MyServerを認識するサーバーに対してSystemトピックでの対話を要求します。hszServNameパラメータとhszSysTopicパラメータは、 以前に作成されている文字列ハンドルです。

HCONV hConv;
HWND hwndParent;
HSZ hszServName;
HSZ hszSysTopic;
.
.
.

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 switch statement to examine transaction types. */
.

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は、 XTYP_CONNECT_CONFIRMトランザクションをサーバーのDDEコールバック関数に送ります。サーバーは、 このトランザクションを処理することによって対話のハンドルを取得できます。

ワイルドカード対話を確立するには、 クライアントは、 サービス名文字列ハンドルやトピック名文字列ハンドルにNULLを指定してDdeConnectを呼び出します。文字列ハンドルのどちらかまたは両方がNULLならば、 DDEMLは、 (XTYP_WILDCONNECTトランザクションをフィルタしているアプリケーションを除く) すべてのDDEアプリケーションのコールバック関数にXTYP_WILDCONNECTトランザクションを送ります。各サーバー アプリケーションは、 HSZPAIR構造体のNULLで終わる配列を識別するハンドルを返すことによって応答しなければなりません。サーバー アプリケーションがDdeNameServiceを呼び出して自分の名前を登録しておらず、 フィルタがオンになっていれば、 アプリケーションはXTYP_WILDCONNECTトランザクションを受け取りません。データ ハンドルについて詳しくは、 データの管理を参照してください。

配列にはサービス名とトピック名の各ペアについて構造体が1つずつなければなりません。この配列は、 クライアントが指定したペアと照合されます。DDEMLは、 ペアの1つを選択して対話を確立し、 その対話を識別するハンドルをクライアントに返します。DDEMLは、 (サーバーがXTYP_CONNECT_CONFIRMトランザクションをフィルタしていなければ) 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 sutructures.
*/

return DdeCreateDataHandle(
idInst,/* instance identifier*/
(LPBYTE) &ahszp, /* points 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関数を呼び出して対話を終了できます。この関数を呼び出すと、 (パートナーがCBF_SKIP_DISCONNECTSフィルタ フラグを指定していなければ) 対話のパートナーはXTYP_DISCONNECTトランザクションを受け取ります。通常、 アプリケーションは、 DdeQueryConvInfo関数を使って終了する対話に関する情報を取得することによってXTYP_DISCONNECTトランザクションに応答します。コールバック関数がXTYP_DISCONNECTトランザクションの処理から戻ると、 対話ハンドルは無効になります。

DDEコールバック関数でXTYP_DISCONNECTトランザクションを受け取ったクライアント アプリケーションは、 DdeReconnect関数を呼び出して対話を再確立できます。クライアントは、 DDEコールバック関数のなかからDdeReconnectを呼び出さなければなりません。

複数の対話

クライアント アプリケーションは、 DdeConnectList関数を使って、 対象とするサーバーがシステムにあるかどうかを判断できます。クライアントがサービス名とトピック名を指定してDdeConnectListを呼び出すと、 DDEMLは、 (XTYP_WILDCONNECTトランザクションをフィルタしているサーバーを除く) そのサービス名に一致するサービス名をもつすべてのサーバーのDDEコールバック関数にXTYP_WILDCONNECTトランザクションを送ります。サーバーのコールバック関数は、 HSZPAIR構造体のNULLで終わる配列を識別するデータ ハンドルを返さなければなりません。この配列には、 サービス名とトピック名の各ペアに対して構造体を1つずつ設定しなければなりません。この配列は、 クライアントが指定したペアと照合されます。DDEMLは、 サーバーが設定した各HSZPAIR構造体について対話を確立し、 クライアントに対話リスト ハンドルを返します。サーバーは、 (XTYP_CONNECT_CONFIRMトランザクションをフィルタしていなければ) XTYP_CONNECT_CONFIRMトランザクションによって対話ハンドルを取得します。

クライアントは、 サービス名やトピック名にNULLを指定してDdeConnectListを呼び出すことができます。サービス名が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 System
トピック. */

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は、 リスト内で対話が重複しないようにします。重複した対話とは、 同じサーバーに対する同じサービス名とトピック名での2つ目の対話です。2つの対話のハンドルは異なりますが、 同じ対話を識別します。

データの管理

DDEではメモリ オブジェクトを使ってデータをアプリケーション間で受渡しするため、 DDEMLは、 DDEアプリケーションがDDEオブジェクトを作成したり管理するための関数のセットを用意しています。

データの交換を行うトランザクションでは、 アプリケーションは、 データを含むローカル バッファを作成してDdeCreateDataHandle関数を呼び出すことによって、 データを供給しなければなりません。この関数は、 DDEオブジェクトを作成してバッファのデータをそのオブジェクトにコピーし、 データ ハンドルを返します。データ ハンドルとは、 DDEオブジェクトのデータにアクセスするために使われるダブルワード値です。DDEオブジェクトのデータを共有するため、 アプリケーションはデータ ハンドルをDDEMLに渡し、 DDEMLは、 データ トランザクションを受け取るアプリケーションのDDEコールバック関数にそのハンドルを渡します。

次の例は、 DDEオブジェクトを作成してそのハンドルを取得する方法を示しています。XTYP_ADVREQトランザクションで、 コールバック関数は、 現在の時刻をASCII文字列に変換してローカル バッファにコピーし、 その文字列を含むDDEオブジェクトを作成します。コールバック関数はDDEオブジェクトのハンドルをDDEMLに返し、 DDEMLはそのハンドルをクライアント アプリケーションに渡します。

typedef struct tagTIME

{

INThour; /* 0 - 11 hours for analog clock */

INThour12; /* 12-hour format */

INThour24; /* 24-hour format */

INTminute;

INTsecond;

INTampm; /* 0 - AM , 1 - PM */

} TIME;

HDDEDATA EXPENTRY DdeCallback(uType, uFmt, hconv, hsz1, hsz2,

hdata, dwData1, dwData2)

UINT uType;

UINT uFmt;

HCONV hconv;

HSZ hsz1;

HSZ hsz2;

HDDEDATA hdata;

DWORD dwData1;

DWORD dwData2;

{

CHAR szBuf[32];

switch (uType) {

case XTYP_ADVREQ:

if ((hsz1 == hszTime && hsz2 == hszNow) &&

(uFmt == CF_TEXT)) {

/* Copy the formatted string to a buffer. */

itoa(tmTime.hour, szBuf, 10);

lstrcat(szBuf, ":");

if (tmTime.minute < 10)

lstrcat(szBuf, "0");

itoa(tmTime.minute, &szBuf[lstrlen(szBuf)], 10);

lstrcat(szBuf, ":");

if (tmTime.second < 10)

strcat(szBuf, "0");

itoa(tmTime.second, &szBuf[lstrlen(szBuf)], 10);

szBuf[lstrlen(szBuf)] = '\0';

/* Create a global object and return its data handle. */

return (DdeCreateDataHandle(

idInst,

(LPBYTE) szBuf,/* instance identifier*/

lstrlen(szBuf) + 1, /* source buffer length*/

0,/* offset from beginning */

hszNow,/* item name string*/

CF_TEXT, /* clipboard format*/

0));/* no creation flags*/

} else

return (HDDEDATA) NULL;

.

. /* Process other transactions. */

.

}

}

受け取り側のアプリケーションは、 DdeAccessData関数にデータ ハンドルを渡すことによって、 DDEオブジェクトを指すポインタを取得します。DdeAccessDataが返すポインタは、 読み取り専用です。アプリケーションは、 このポインタを使ってデータを調べ、 DdeUnaccessData関数を呼び出してポインタを無効化します。データをローカル バッファにコピーするには、 DdeGetData関数を呼び出します。

次の例は、 hDataパラメータが識別するDDEオブジェクトを指すポインタを取得してその内容をローカル バッファにコピーし、 ポインタを無効化します。

HDDEDATA hdata;
LPBYTE lpszAdviseData;
DWORD cbDataLen;
DWORD i;
char szData[32];

.
.
.
case XTYP_ADVDATA:

lpszAdviseData = DdeAccessData(hdata, &cbDataLen);
for (i = 0; i < cbDataLen; i++)
szData[i] = *lpszAdviseData++;
DdeUnaccessData(hdata);
return (HDDEDATA) TRUE;
.
.
.

通常、 データ ハンドルを作成したアプリケーションがハンドルをDDEMLに渡すと、 そのハンドルは作成側のアプリケーションでは無効になります。これは、 1つのアプリケーションとだけデータを共有しなければならないアプリケーションで役立ちます。しかし、 複数のアプリケーションとデータを共有しなければならないときは、 作成側のアプリケーションはDdeCreateDataHandleHDATA_APPOWNEDを指定してください。これによって、 DDEオブジェクトの所有権が作成側のアプリケーションに与えられ、 DDEMLはハンドルを無効化しません。アプリケーションはデータ ハンドルを何度も渡すことができますが、 DdeCreateDataHandleの呼び出しは1回だけにしてください。

DdeCreateDataHandleafCmdパラメータにHDATA_APPOWNEDフラグを指定した場合、 アプリケーションは、 ハンドルをDDEMLに渡したかどうかにかかわらず、 終了する前にDdeFreeDataHandle関数を呼び出してメモリ ハンドルを解放しなければなりません。作成したにもかかわらずDDEMLに渡さなかったハンドルも、 終了する前にDdeFreeDataHandle関数を呼び出してメモリ ハンドルをすべて解放しなければなりません。

アプリケーションは、 DDEオブジェクトのハンドルをDDEMLにまだ渡していなければ、 DdeAddData関数を使ってオブジェクトにデータを追加したりデータを上書きすることができます。通常、 アプリケーションは、 初期化されていないDDEオブジェクトにDdeAddDataを使ってデータを設定します。アプリケーションがデータ ハンドルをDDEMLに渡すと、 ハンドルが識別するDDEオブジェクトは変更できなくなり、 解放しかできなくなります。

トランザクションの管理

クライアントは、 サーバーとの対話を確立したら、 トランザクションを送ってサーバーからデータやサービスを取得できます。

要求トランザクション

クライアント アプリケーションは、 XTYP_REQUESTトランザクションを使ってサーバー アプリケーションにデータ項目を要求できます。クライアントは、 XTYP_REQUESTトランザクションの種類と必要なデータ項目を指定して、 DdeClientTransaction関数を呼び出します。

DDEMLは、 クライアントが要求したトピック名、 項目名、 データ形式を指定したXTYP_REQUESTトランザクションをサーバーに送ります。要求されたトピック、 項目、 形式をサポートするサーバーは、 その項目の現在の値を識別するデータ ハンドルを返さなければなりません。DDEMLは、 このハンドルをDdeClientTransactionの戻り値としてクライアントに渡します。要求されたトピック、 項目、 形式をサポートしないサーバーは、 NULLを返さなければなりません。

DdeClientTransactionは、 lpdwResultパラメータを使ってトランザクション状態フラグをクライアントに返します。サーバーがXTYP_REQUESTトランザクションを処理しなければ、 DdeClientTransactionNULLを返し、 lpdwResultDDE_FNOTPROCESSEDフラグかDDE_FBUSYフラグを指します。DDE_FNOTPROCESSEDフラグが返されたときは、 サーバーがトランザクションを処理しなかった理由をクライアントが判断することはできません。

XTYP_REQUESTトランザクションをサポートしないサーバーは、 DdeInitialize関数でCBF_FAIL_REQUESTSフィルタ フラグを指定しなければなりません。これによって、 DDEMLはこのトランザクションをサーバーに送らなくなります。

ポーク トランザクション

要求されていないデータをサーバーに送るには、 クライアントは、 DdeClientTransactionを使ってXTYP_POKEトランザクションをサーバーのコールバック関数に送ります。

クライアント アプリケーションは、 まず、 サーバーに送るデータを設定したバッファを作成し、 そのバッファを指すポインタをDdeClientTransactionのパラメータとして渡します。また、 クライアントは、 DdeCreateDataHandle関数を使ってデータを識別するデータ ハンドルを取得し、 そのハンドルをDdeClientTransactionに渡すこともできます。どちらの場合も、 クライアントは、 トピック名、 項目名、 データ形式を指定してDdeClientTransactionを呼び出します。

DDEMLは、 クライアントが要求したトピック名、 項目名、 データ形式を指定したXTYP_POKEトランザクションをサーバーに送ります。サーバーは、 要求されたデータ項目と形式を受け付ける場合、 DDE_FACKを返します。要求されたデータを却下する場合は、 サーバーはDDE_FNOTPROCESSEDを返します。ビジー状態のためデータを受け付けられないときは、 サーバーはDDE_FBUSYを返します。

DdeClientTransactionが戻ったら、 クライアントは、 lpdwResultパラメータを使ってトランザクション状態フラグにアクセスできます。このフラグがDDE_FBUSYならば、 クライアントはトランザクションを後でもう一度送らなければなりません。

XTYP_POKEトランザクションをサポートしないサーバーは、 DdeInitializeCBF_FAIL_POKESフィルタ フラグを指定しなければなりません。これによって、 DDEMLは、 このトランザクションをサーバーに送らなくなります。

アドバイズ トランザクション

クライアント アプリケーションは、 DDEMLを使って、 サーバー アプリケーションの項目とのリンクを確立できます。このようなリンクが確立されると、 サーバーは、 リンクされている項目の更新を定期的に (通常は、 サーバー アプリケーションの項目の値が変化するたびに) クライアントに送ります。これによって、 2つのアプリケーションの間にアドバイズ ループが確立され、 クライアントがループを終了するまで続きます。

アドバイス ループには、 「ホット」と「ウォーム」の2種類があります。ホット アドバイズ ループでは、 サーバーは、 変化した値を識別するデータ ハンドルをすぐに送ります。ウォーム アドバイズ ループでは、 サーバーは、 項目の値が変化したことをクライアントに通知しますが、 クライアントが要求するまではデータ ハンドルを送りません。

サーバーとのホット アドバイズ ループを要求するには、 クライアントは、 XTYP_ADVSTARTトランザクションの種類を指定してDdeClientTransactionを呼び出します。ウォーム アドバイズ ループを要求するには、 クライアントは、 XTYP_ADVSTARTトランザクションの種類にXTYPF_NODATAフラグを組み合わせなければなりません。どちらの場合も、 DDEMLは、 XTYP_ADVSTARTトランザクションをサーバーのDDEコールバック関数に送ります。サーバーのDDEコールバック関数は、 XTYP_ADVSTARTトランザクションのパラメータ (要求されている形式、 トピック名、 項目名など) を調べて、 アドバイズ ループを許可するときはTRUEを、 拒否するときはFALSEを返します。

アドバイズ ループが確立されたら、 サーバー アプリケーションは、 要求された項目名の項目の値が変化するたびに、 DdePostAdvise関数を呼び出します。これによって、 サーバーのDDEコールバック関数にXTYP_ADVREQトランザクションが送られます。サーバーのDDEコールバック関数は、 データ項目の新しい値を識別するデータ ハンドルを返します。すると、 DDEMLは、 XTYP_ADVDATAトランザクションをクライアントのDDEコールバック関数に送って、 指定された項目が変化したことをクライアントに通知します。

クライアントがホット アドバイズ ループを要求したときは、 DDEMLは、 変化した項目のデータ ハンドルをXTYP_ADVDATAトランザクションでクライアントに送ります。ウォーム アドバイズ ループの場合は、 クライアントはXTYP_REQUESTトランザクションを送ってデータ ハンドルを取得します。

サーバーは、 クライアントが新しいデータを処理するよりも速くデータを更新する場合があります。データに対して時間のかかる処理を行うクライアントでは、 これは問題になります。この場合、 クライアントは、 アドバイズ ループを要求するときにXTYPF_ACKREQフラグを指定します。このフラグを指定すると、 サーバーは、 クライアントがデータを受け取って処理したことを示すまで待ってから、 次のデータ項目を送ります。XTYPF_ACKREQフラグを指定して確立されたアドバイズ ループは、 高速なサーバーとでも追随が可能ですが、 場合によっては更新が失われる可能性があります。XTYPF_ACKREQフラグを指定せずに確立されたアドバイズ ループでは、 クライアントがサーバーに追随する限り更新が失われることはありません。

アドバイズ ループを終了するには、 クライアントは、 XTYP_ADVSTOPトランザクションの種類を指定してDdeClientTransactionを呼び出します。

アドバイズ ループをサポートしないサーバーは、 DdeInitialize関数でCBF_FAIL_ADVISESフィルタ フラグを指定しなければなりません。これによって、 DDEMLは、 XTYP_ADVSTARTトランザクションとXTYP_ADVSTOPトランザクションをサーバーに送らなくなります。

実行トランザクション

クライアントは、 XTYP_EXECUTEトランザクションを使って、 コマンドをサーバーに実行させることができます。

サーバー コマンドを実行するには、 クライアントは、 まず、 サーバーに実行させるコマンド文字列を設定したバッファを作成し、 そのバッファを指すポインタまたはバッファを識別するデータ ハンドルを指定してDdeClientTransactionを呼び出します。このほかに必要なパラメータは、 対話ハンドル、 項目名の文字列ハンドル、 形式の指定、 XTYP_EXECUTEトランザクションの種類です。実行データを渡すためのデータ ハンドルを作成するときは、 DdeCreateDataHandle関数のhszItemパラメータにはNULLを、 uFmtパラメータには0を指定してください。

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が戻るまではほかの同期トランザクションを送ることはできません。

非同期トランザクションを送るには、 クライアントは、 TIMEOUT_ASYNCフラグを指定してDdeClientTransactionを呼び出します。この関数は、 トランザクションが開始されると戻り、 トランザクション識別子をクライアントに返します。サーバーが非同期トランザクションの処理を終了すると、 DDEMLは、 クライアントにXTYP_XACT_COMPLETEトランザクションを送ります。DDEMLXTYP_XACT_COMPLETEトランザクションでクライアントに渡すパラメータには、 トランザクション識別子があります。クライアントは、 DdeClientTransactionが返したトランザクション識別子とこの識別子を比較して、 どのサーバーが処理を終了した非同期トランザクションを識別できます。

クライアントは、 非同期トランザクションの処理の補助としてDdeSetUserHandle関数を使うことができます。クライアントは、 この関数によって、 アプリケーション定義のダブルワード値を対話ハンドルとトランザクション識別子に関連付けることができます。クライアントは、 XTYP_XACT_COMPLETEトランザクションで、 DdeQueryConvInfo関数を使ってアプリケーション定義のダブルワード値を取得できます。このため、 アプリケーションは、 アクティブなトランザクション識別子のリストを管理する必要はありません。

非同期トランザクションによるデータの要求をクライアントが正常に完了しても、 DDEMLは、 クライアントが受け取ったデータの処理を終了したかどうかを判断できません。このため、 クライアント アプリケーションは、 受け取ったデータ ハンドルをDdeFreeDataHandle関数に渡して、 ハンドルが使われなくなったことをDDEMLに通知しなければなりません。非同期トランザクションで返されるハンドルは、 事実上はクライアントが所有します。

サーバーが非同期トランザクションを時間内に処理しなければ、 クライアントは、 DdeAbandonTransaction関数を呼び出してトランザクションを中断できます。DDEMLは、 そのトランザクションに関するリソースをすべて解放し、 サーバーがトランザクションの処理を完了しても結果を廃棄します。非同期トランザクションでタイム アウトが発生すると、 トランザクションは事実上取り消されます。

非同期トランザクションは、 計算などの大量の処理を実行しながら多量のDDEトランザクションを送らなければならないアプリケーションのために用意されています。また、 非同期トランザクションは、 割り込みなしで完了しなければならない作業がほかにあり、 DDEトランザクションの処理を一時的に中断しなければならない場合に役立ちます。そのほかの場合は、 同期トランザクションを使ってください。

同期トランザクションは、 非同期トランザクションよりも管理しやすく、 高速です。しかし、 同期トランザクションは一度に1つしか実行できないのに対して、 非同期トランザクションは、 同時に複数を実行できます。同期トランザクションでは、 サーバーが低速な場合、 クライアントはサーバーの応答を待つ間アイドル状態になります。

また、 同期トランザクションではクライアントはモーダル ループに入るため、 アプリケーション自身のメッセージ ループのメッセージのフィルタ処理がバイパスされてしまいます。クライアントがMSGF_DDEMGRメッセージ フィルタを指定していれば、 メッセージのフィルタ処理はバイパスされません。

同期トランザクション モーダル ループの主な危険性は、 ダイアログ ボックスが作成したモーダル ループと干渉することです。アプリケーションで注意深くコーディングすることにより、 この状況を避けることはできます。しかし、 ダイナミック リンク ライブラリ (DLL) では、 呼び出し側のアプリケーションがどういう動きをするか予想することはできません。DLLのコードが同期トランザクションを実行しているときに、 呼び出し側のアプリケーションがダイアログ ボックスを表示するかもしれません。ダイナミック リンク ライブラリ (DLL) DDEMLを使うときは、 必ず非同期トランザクションを使ってください。

トランザクションの制御

アプリケーションは、 自分のDDEコールバック関数に対する、 特定の対話ハンドルに関するトランザクションを中断したり、 対話ハンドルに関係なくすべてのトランザクションを中断できます。これは、 時間のかかる処理が必要なトランザクションを受け取る場合に役立ちます。そのような場合、 アプリケーションは、 CBR_BLOCKを返してそのトランザクションの対話ハンドルに関するそれ以降のトランザクションを中断し、 ほかの対話を処理できます。

処理が完了したら、 DdeEnableCallback関数を呼び出して、 中断した対話に関するトランザクションを再開します。DdeEnableCallbackを呼び出すと、 DDEMLは、 アプリケーションが中断した対話のトランザクションを送りなおします。このため、 アプリケーションは、 後で取得可能な方法でトランザクションの結果を格納し、 トランザクションを再処理せずに結果を返さなければなりません。

特定の対話ハンドルに関するトランザクションをすべて中断するには、 そのハンドルとEC_DISABLEフラグを指定してDdeEnableCallbackを呼び出します。NULLハンドルを指定すると、 すべての対話のすべてのトランザクションが中断されます。

対話が中断すると、 DDEMLは、 その対話のトランザクションをトランザクション キューに保存します。アプリケーションが対話を再開すると、 DDEMLは、 保存しておいたトランザクションをキューから削除して、 適切なコールバック関数に渡します。トランザクション キューは十分な大きさにしてありますが、 トランザクションが失われないように、 中断した対話はできるだけ早く再開してください。

通常のトランザクション処理を再開するには、 EC_ENABLEALLフラグを指定してDdeEnableCallbackを呼び出します。トランザクション処理の再開をより詳細に制御したいときは、 EC_ENABLEONEフラグを指定します。このフラグを指定すると、 トランザクション キューからトランザクションが1つ削除され、 適切なコールバック関数に渡されます。そのトランザクションが処理されると、 対話は再び中断されます。

EC_ENABLEONEフラグと対話ハンドルを指定してDdeEnableCallbackを呼び出すと、 トランザクションを1つ処理した後、 その対話だけがブロックされます。NULL対話ハンドルを指定すると、 いずれかの対話のトランザクションを1つ処理した後、 すべての対話がブロックされます。

トランザクションのクラス

DDEMLのトランザクションには、 4つのクラスがあります。各クラスは、 XCLASS_で始まる定数で識別されます。クラス定数は、 トランザクション定数と組み合わせて受け取り側のDDEコールバック関数に渡されます。

コールバック関数は、 トランザクションのクラスによって、 トランザクションを処理した場合に返すべき値を判断できます。4つのトランザクション クラスのそれぞれの戻り値とトランザクションの種類を次の表に示します。

クラス 戻り値 トランザクション

XCLASS_BOOLTRUEまたはFALSEXTYP_ADVSTARTXTYP_CONNECT

XCLASS_DATA データ ハンドル、 CBR_BLOCK戻りコード、 NULLXTYP_ADVREQ XTYP_REQUESTXTYP_WILDCONNECT

XCLASS_FLAGS トランザクション フラグ (DDE_FACK DDE_FBUSY DDE_FNOTPROCESSED)XTYP_ADVDATA XTYP_EXECUTEXTYP_POKE

XCLASS_NOTIFICATION なしXTYP_ADVSTOP XTYP_CONNECT_CONFIRMXTYP_DISCONNECTXTYP_ERRORXTYP_REGISTER XTYP_UNREGISTERXTYP_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 サーバーXTYP_CONNECTトランザクションやXTYP_WILDCONNECTトランザクションに対してサーバーがTRUEを返しました。

XTYP_DISCONNECT クライアント、 サーバー対話のパートナーがDdeDisconnect関数を呼び出しました。このトランザクションは、 両方のパートナーが受け取ります。

XTYP_ERROR クライアント、 サーバー重大なエラーが発生しました。DDEMLが処理を続行するためのリソースが不足している可能性があります。

XTYP_EXECUTE サーバークライアントがXTYP_EXECUTEトランザクションの種類を指定してDdeClientTransaction関数を呼び出しました。

XTYP_MONITORDDE監視アプリケーションシステムでDDEイベントが発生しました。DDE監視アプリケーションについて詳しくは、 監視アプリケーションを参照してください。

XTYP_POKE サーバークライアントがXTYP_POKEトランザクションの種類を指定してDdeClientTransaction関数を呼び出しました。

XTYP_REGISTER クライアント、 サーバーサーバー アプリケーションがDdeNameService関数を使ってサービス名を登録しました。

XTYP_REQUEST サーバークライアントがXTYP_REQUESTトランザクションの種類を指定してDdeClientTransaction関数を呼び出しました。

XTYP_UNREGISTER クライアント、 サーバーサーバーがDdeNameServiceを使ってサービス名を登録解除しました。

XTYP_WILDCONNECT サーバークライアントがサービス名やトピック名にNULLを指定してDdeConnect関数かDdeConnectList関数を呼び出しました。

XTYP_XACT_COMPLETE クライアントクライアントがTIMEOUT_ASYNCフラグを指定してDdeClientTransactionを呼び出したときに送られた非同期トランザクションが完了しました。

エラーの検出

DDEML関数が失敗したら、 DdeGetLastError関数を呼び出して失敗の原因を判断してください。DdeGetLastErrorは、 最近のエラーの原因を示す値を返します。

監視アプリケーション

DDEMLのアプリケーション プログラミング インターフェイス (API) を使って、 システムのDDE動作を監視するアプリケーションを作成できます。DDE監視アプリケーションは、 ほかのDDEMLアプリケーションと同様に、 DDEコールバック関数を持ちます。DDEMLは、 DDEイベントが発生するたびに監視アプリケーションのDDEコールバック関数を呼び出して、 そのイベントに関する情報を渡します。通常、 監視アプリケーションは、 情報をウィンドウに表示したりファイルに書き込みます。

DDEMLからの通知を受け取るには、 APPCLASS_MONITORフラグを指定してDdeInitialize関数を呼び出すことによって自分自身をDDE監視アプリケーションとして登録しなければなりません。この関数には、 DDEMLがアプリケーションのコールバック関数に通知するイベントの種類を示すフラグも指定できます。アプリケーションが指定可能な監視フラグを次の表に示します。

フラグ 意味

MF_CALLBACKS システム内のDDEコールバック関数のいずれかにトランザクションが送られると、 監視コールバック関数に通知します。

MF_CONV 対話が確立されるか終了するとコールバック関数に通知します。

MF_ERRORSDDEMLエラーが発生するとコールバック関数に通知します。

MF_HSZ_INFO DDEMLアプリケーションが文字列ハンドルを作成または解放したり、 文字列ハンドルの使用カウントを増やしたときにコールバック関数に通知します。また、 アプリケーションがDdeUninitialize関数を呼び出したために文字列ハンドルが解放されたときにも通知します。

MF_LINKS アドバイズ ループが開始したり終了したときにコールバック関数に通知します。

MF_POSTMSGS システムやアプリケーションがDDEメッセージをポストするとコールバック関数に通知します。

MF_SENDMSGS システムやアプリケーションがDDEメッセージを送るとコールバック関数に通知します。

次の例は、 DDEコールバック関数がすべてのDDEイベントの通知を受け取るようにDDE監視アプリケーションを登録する方法を示しています。

DWORD idInst;

PFNCALLBACK lpDdeProc;

hInst = hInstance;

if (DdeInitialize(

(LPDWORD) &idInst,/* instance identifier*/

DDECallback,/* points to callback function*/

APPCLASS_MONITOR |/* this is a monitoring application */

MF_CALLBACKS|/* monitor callback functions*/

MF_CONV|/* monitor conversation data*/

MF_ERRORS|/* monitor DDEML errors*/

MF_HSZ_INFO|/* monitor data handle activity*/

MF_LINKS|/* monitor advise loops*/

MF_POSTMSGS|/* monitor posted DDE messages*/

MF_SENDMSGS,/* monitor sent DDE messages*/

0))/* reserved*/

return FALSE;

DDEMLは、 監視アプリケーションのDDEコールバック関数にXTYP_MONITORトランザクションを送ることによって、 監視アプリケーションにDDEイベントを通知します。このトランザクションでは、 DDEMLは、 発生したDDEイベントの種類を示す監視フラグと、 イベントに関する詳細な情報を含むDDEオブジェクトのハンドルを渡します。DDEMLは、 アプリケーションがDDEオブジェクトから情報を抽出するための構造体のセットを用意しています。DDEイベントの各種類について対応する構造体があります。各構造体の説明を次の表に示します。

構造体 説明

MONCBSTRUCT トランザクションに関する情報を示します。

MONCONVSTRUCT 対話に関する情報を示します。

MONERRSTRUCT 最新のDDEエラーに関する情報を示します。

MONLINKSTRUCT アドバイズ ループに関する情報を示します。

MONHSZSTRUCT 文字列ハンドルに関する情報を示します。

MONMSGSTRUCT 送られたりポストされたDDEメッセージに関する情報を示します。

次の例は、 DDE監視アプリケーションのDDEコールバック関数を示しています。このコールバック関数は、 各文字列ハンドル イベントに関する情報を書式化し、 ウィンドウに表示します。この関数は、 MONHSZSTRUCT構造体を使ってDDEオブジェクトから情報を抽出します。

HDDEDATA CALLBACK DDECallback(uType, uFmt, hconv, hsz1, hsz2,
hdata, dwData1, dwData2)
UINT uType;
UINT uFmt;
HCONV hconv;
HSZ hsz1;
HSZ hsz2;
HDDEDATA hdata;
DWORD dwData1;
DWORD dwData2;
{
LPVOID lpData;
CHAR *szAction;
CHAR szBuf[256];
DWORD cb;

switch (uType) {
case XTYP_MONITOR:

/* Obtain a pointer to the global memory object. */

if (lpData = DdeAccessData(hdata, &cb)) {

/* Examine the monitor flag. */

switch (dwData2) {
case MF_HSZ_INFO:

#define PHSZS ((MONHSZSTRUCT FAR *)lpData)

/*
* The global memory object contains
* string-handle data. Use the MONHSZSTRUCT
* structure to access the data.
*/

switch (PHSZS->fsAction) {

/*
* Examine the action flags to
* determine the action performed on
* the handle.
*/

case MH_CREATE:
szAction = "Created";
break;

case MH_KEEP:
szAction = "Incremented";
break;

case MH_DELETE:
szAction = "Deleted";
break;

case MH_CLEANUP:
szAction = "Cleaned up";
break;

default:
DdeUnaccessData(hdata);
return (HDDEDATA) 0;
}

/* Write formatted output to a buffer. */

wsprintf(szBuf,
"Handle %s, Task: %x, Hsz: %lx(%s)",
(LPSTR) szAction, PHSZS->hTask,
PHSZS->hsz, (LPSTR) PHSZS->str);
.
. /* Display text or write to a file. */
.

break;

#undef PHSZS

.
. /* Process other MF_* flags. */
.

default:
break;
}
}

/* Free the global memory object. */

DdeUnaccessData(hdata);
break;

default:
break;
}
return (HDDEDATA) 0;
}

動的データ交換管理関数

動的データ交換の管理に使われる関数を次に示します。

DdeAbandonTransaction

DdeAccessData

DdeAddData

DdeCallback

DdeClientTransaction

DdeCmpStringHandles

DdeConnect

DdeConnectList

DdeCreateDataHandle

DdeCreateStringHandle

DdeDisconnect

DdeDisconnectList

DdeEnableCallback

DdeFreeDataHandle

DdeFreeStringHandle

DdeGetData

DdeGetLastError

DdeInitialize

DdeKeepStringHandle

DdeNameService

DdePostAdvise

DdeQueryConvInfo

DdeQueryNextServer

DdeQueryString

DdeReconnect

DdeSetUserHandle

DdeUnaccessData

DdeUninitialize

▲ページトップに戻る

【本を読んでもよくらからない】 … 個別指導でわかりやすくお教えします