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

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

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

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

Windows API マルチ ドキュメント インターフェイスの概要

マルチ ドキュメント インターフェイス (MDI) は、 Microsoft(R) Windows(TM) 用アプリケーションの標準ユーザー インターフェイスを定義する仕様です。ユーザーは、 MDIアプリケーションによって、 複数のドキュメントを同時に操作できます。各ドキュメントは、 アプリケーションのメイン ウィンドウのクライアント領域に、 独立した子ウィンドウとして表示されます。MDIアプリケーションには、 複数のテキスト ドキュメントを操作可能なワード プロセッサ アプリケーションや、 複数のグラフや表を操作可能な表計算アプリケーションなどがあります。

Windowsは、 MDIアプリケーションを広範囲にサポートしています。このトピックでは、 MDIアプリケーションの構造と、 Microsoft(R) Win32(R) アプリケーション プログラミング インターフェイス (API) の組み込みMDIサポートの利用方法について説明します。

次に示すトピックでは、 Win32のマルチ ドキュメント インターフェイスに付いて説明しています。

  • フレーム ウィンドウ、 クライアント ウィンドウ、 子ウィンドウ
  • 子ウィンドウの作成
  • 子ウィンドウのアクティブ化
  • メニュー
  • アクセラレータ
  • 子ウィンドウのサイズと配置
  • アイコン タイトル ウィンドウ
  • 子ウィンドウのデータ
  • 子ウィンドウやフレーム ウィンドウのクラスの登録
  • フレーム ウィンドウと子ウィンドウの作成
  • メイン メッセージ ループの作成
  • フレーム ウィンドウ プロシージャの作成
  • 子ウィンドウ プロシージャの作成
  • 子ウィンドウの作成
  • マルチ ドキュメント インターフェイス関数とメッセージ

 

 

フレーム ウィンドウ、 クライアント ウィンドウ、 子ウィンドウ

MDIアプリケーションには、 フレーム ウィンドウ、 クライアント ウィンドウ、 子ウィンドウの3種類のウィンドウがあります。子ウィンドウは複数存在します。「フレーム ウィンドウ」は、 アプリケーションのメイン ウィンドウに似ており、 サイズ変更境界線、 タイトル バー、 システム メニュー (コントロール メニュー)、 アイコン化ボタン、 最大表示ボタンがあります。アプリケーションは、 フレーム ウィンドウのウィンドウ クラスを登録し、 それをサポートするウィンドウ プロシージャを用意しなければなりません。

MDIアプリケーションは、 フレーム ウィンドウのクライアント領域には出力を表示しません。フレーム ウィンドウのクライアント領域には、 MDIクライアント ウィンドウを表示します。「MDIクライアント ウィンドウ」は特殊な子ウィンドウで、 登録済みのウィンドウ クラスのMDICLIENTに属します。クライアント ウィンドウは、 フレーム ウィンドウの子ウィンドウで、 MDI子ウィンドウの背景として動作します。また、 子ウィンドウの作成や操作もサポートします。たとえば、 MDIアプリケーションが子ウィンドウを作成、 アクティブ化、 最大化するには、 MDIクライアント ウィンドウにメッセージを送ります。

ユーザーがドキュメントをオープンしたり作成すると、 クライアント ウィンドウは、 そのドキュメント用の子ウィンドウを作成します。クライアント ウィンドウは、 アプリケーションのすべてのMDI子ウィンドウの親ウィンドウです。各子ウィンドウには、 サイズ変更境界線、 タイトル バー、 システム メニュー、 アイコン化ボタン、 最大表示ボタンがあります。子ウィンドウはクリップされるため、 子ウィンドウはクライアント ウィンドウ内に制限され、 クライアント ウィンドウ外に表示することはできません。

MDIアプリケーションは、 何種類かのドキュメントをサポートできます。たとえば、 通常の表計算アプリケーションでは、 ユーザーはグラフと表を同時に操作できます。MDIアプリケーションは、 サポートするドキュメントの種類ごとに、 子ウィンドウ クラスを登録して、 そのクラスに属するウィンドウをサポートするウィンドウ プロシージャを用意しなければなりません。ウィンドウ クラスについて詳しくは、 ウィンドウ クラスの概要を参照してください。ウィンドウ プロシージャについて詳しくは、 ウィンドウ プロシージャの概要を参照してください。

典型的なMDIアプリケーションを次に示します。このアプリケーションの名前はMultipadです。

 

 

子ウィンドウの作成

子ウィンドウを作成するには、 CreateMDIWindow関数を呼び出すか、 WM_MDICREATEメッセージをMDIクライアント ウィンドウに送ります。MDIアプリケーションのスレッドは、 CreateMDIWindowを呼び出して、 子ウィンドウを別のスレッドで作成できます。WM_MDICREATEメッセージは、 同じスレッドのコンテキストでしか使われません。

子ウィンドウを破棄するには、 WM_MDIDESTROYメッセージをMDI子ウィンドウに送ります。

 

 

子ウィンドウのアクティブ化

クライアント ウィンドウには複数の子ウィンドウを同時に表示できますが、 アクティブにできるのは1つだけです。アクティブな子ウィンドウはほかのすべての子ウィンドウよりも前に表示され、 境界線が強調表示されます。

アクティブでないウィンドウをユーザーがクリックすると、 そのウィンドウはアクティブになります。MDIアプリケーションが子ウィンドウをアクティブにするには、 WM_MDIACTIVATEメッセージをMDIクライアント ウィンドウに送ります。クライアント ウィンドウは、 このメッセージを処理するとき、 アクティブ化する子ウィンドウのウィンドウ プロシージャと非アクティブ化する子ウィンドウのウィンドウ プロシージャにWM_MDIACTIVATEメッセージを送ります。

Windowsは、 オーバーラップしているウィンドウの重なり順序での各子ウィンドウの位置を管理しています。この重なり順序は、 Z順序とも呼ばれます。ユーザーは、 アクティブ ウィンドウのコントロール メニューから[次のウィンドウに移る]コマンドを選択することによって、 Z順序での次の子ウィンドウをアクティブ化できます。アプリケーションがZ順序の次 (または前) の子ウィンドウをアクティブ化するには、 クライアント ウィンドウにWM_MDINEXTメッセージを送ります。

アクティブな子ウィンドウのハンドルを取得するには、 クライアント ウィンドウにWM_MDIGETACTIVEメッセージを送ります。

 

 

 

メニュー

MDIアプリケーションのフレーム ウィンドウには、 [ウィンドウ]メニューを含むメニュー バーと、 それに対応するポップ アップ メニューを付加してください。[ウィンドウ]ポップ アップ メニューには、 クライアント ウィンドウ内の子ウィンドウを配置するコマンドや、 子ウィンドウをすべてクローズするコマンドを用意してください。通常、 MDIアプリケーションの[ウィンドウ]メニューには、 次の表に示す項目があります。

 

メニュー項目 用途

[並べて表示] 子ウィンドウをタイル形式で配置し、 各子ウィンドウの全体がクライアント ウィンドウに表示されるようにします。

[重ねて表示] 子ウィンドウをカスケード形式で配置します。子ウィンドウは互いに重なりますが、 各子ウィンドウのタイトル バーは重なりません。

[アイコンの整列] アイコン化されている子ウィンドウのアイコンをクライアント ウィンドウの底辺に沿って並べます。

[すべて閉じる] 子ウィンドウをすべてクローズします。

 

 

Windowsは、 子ウィンドウを作成すると、 自動的に新しいメニュー項目を[ウィンドウ]メニューに追加します。このメニュー項目のテキストは、 新しい子ウィンドウのタイトル バーのテキストと同じです。ユーザーがこのメニュー項目を選択すると、 対応する子ウィンドウがアクティブになります。Windowsは、 子ウィンドウを破棄するとき、 対応するメニュー項目を自動的に[ウィンドウ]メニューから削除します。

Windowsは、 10個までのメニュー項目を[ウィンドウ]メニューに追加します。10番目の子ウィンドウを作成するとき、 Windowsは、 [ウィンドウ]メニューに[その他のウィンドウ]項目を追加します。この項目を選択すると、 [ウィンドウの選択]ダイアログ ボックスが表示されます。このダイアログ ボックスには、 現在利用可能なすべてのMDI子ウィンドウのタイトルを示すリスト ボックスがあります。ユーザーは、 リスト ボックスから子ウィンドウのタイトルを選択することによって、 そのウィンドウをアクティブ化できます。

何種類かの子ウィンドウをサポートする場合は、 アクティブなウィンドウに関連する操作に合わせてメニューを修正してください。これを行うには、 アプリケーションがサポートする子ウィンドウの種類それぞれについてメニュー リソースを用意します。別の種類の子ウィンドウがアクティブ化されたら、 クライアント ウィンドウにWM_MDISETMENUメッセージを送って、 対応するメニューのハンドルを渡してください。

子ウィンドウが存在しないときは、 メニュー バーの項目はドキュメントを作成またはオープンする項目だけにしてください。

ユーザーがカーソル キーを使ってMDIアプリケーションのメニューを操作しているときは、 キーの動作は通常のアプリケーションのメニューの操作とは異なります。MDIアプリケーションでは、 アプリケーションのコントロール メニュー、 アクティブな子ウィンドウのコントロール メニュー、 メニュー バーの最初の項目の順に移動します。

 

 

アクセラレータ

子ウィンドウのアクセラレータ キーを受け取って処理するには、 アプリケーションのメッセージ ループでTranslateMDISysAccel関数を呼び出してください。ループでは、 TranslateMDISysAccelを呼び出してから、 TranslateAccelerator関数やDispatchMessage関数を呼び出してください。

MDI子ウィンドウのコントロール メニューのアクセラレータ キーは、 MDI以外の子ウィンドウのアクセラレータ キーとは異なります。MDI子ウィンドウでは、 Alt+-(マイナス) キーはコントロール メニューをオープンし、 Ctrl+F4キーはアクティブな子ウィンドウをクローズします。また、 Ctrl+F6キーは次の子ウィンドウをアクティブ化します。

 

 

子ウィンドウのサイズと配置

MDIアプリケーションは、 MDIクライアント ウィンドウにメッセージを送ることによって、 子ウィンドウのサイズや位置を制御できます。アクティブな子ウィンドウを最大化するには、 WM_MDIMAXIMIZEメッセージをクライアント ウィンドウに送ります。子ウィンドウを最大化すると、 そのクライアント領域はMDIクライアント ウィンドウいっぱいになります。さらに、 Windowsは、 子ウィンドウのタイトル バーを自動的に非表示にして、 子ウィンドウのコントロール メニュー アイコンと復元アイコンをMDIアプリケーションのメニュー バーに付加します。クライアント ウィンドウを (最大化する前の) 元のサイズと位置に復元するには、 WM_MDIRESTOREメッセージを送ります。

MDIアプリケーションは、 子ウィンドウをカスケード形式かタイル形式で配置できます。カスケード形式の子ウィンドウは、 重なって表示されます。重なり順の最下位のウィンドウは画面の左上隅に表示され、 残りのウィンドウは、 各子ウィンドウのタイトルと左側の境界線が見えるように、 水平と垂直にずらして表示されます。子ウィンドウをカスケード形式で配置するには、 WM_MDICASCADEメッセージを送ります。通常、 このメッセージは、 ユーザーが[ウィンドウ]メニューから[重ねて表示]コマンドを選択したときに送ります。

Windowsは、 子ウィンドウをタイル表示するとき、 ウィンドウどうしが重ならないように各子ウィンドウの全体を表示します。必要に応じてウィンドウのサイズを変更し、 すべてのウィンドウがクライアント ウィンドウに収まるようにします。子ウィンドウをタイル形式で配置するには、 クライアント ウィンドウにWM_MDITILEメッセージを送ります。通常、 このメッセージは、 ユーザーが[ウィンドウ]メニューから[並べて表示]コマンドを選択したときに送ります。

MDIアプリケーションは、 サポートする子ウィンドウの種類それぞれについて個別のアイコンを用意してください。アイコンは、 子ウィンドウ クラスを登録するときに指定します。Windowsは、 子ウィンドウをアイコン化するとき、 子ウィンドウのアイコンをクライアント ウィンドウの下部に自動的に表示します。子ウィンドウのアイコンを配置するようにWindowsに指示するには、 クライアント ウィンドウにWM_MDIICONARRANGEメッセージを送ります。通常、 このメッセージは、 ユーザーが[ウィンドウ]メニューから[アイコンの整列]コマンドを選択したときに送ります。

 

 

アイコン タイトル ウィンドウ

MDI子ウィンドウはアイコン化される可能性がありますが、 アイコン タイトル ウィンドウは通常のMDI子ウィンドウと同様には操作しないでください。MDI子ウィンドウを列挙すると、 アイコン タイトル ウィンドウも同時に列挙されます。しかし、 アイコン タイトル ウィンドウはMDI子ウィンドウが所有しており、 ほかの子ウィンドウとは異なります。

子ウィンドウがアイコン タイトル ウィンドウかどうか調べるには、 GW_OWNERインデックスを指定してGetWindow関数を呼び出します。アイコン タイトル ウィンドウ以外のウィンドウはNULLを返します。メニューやダイアログ ボックスもオーナー付きウィンドウであるため、 トップ レベル ウィンドウではこのテストは不十分です。

 

 

子ウィンドウのデータ

子ウィンドウの個数はユーザーがオープンしているドキュメントの個数によって異なるため、 MDIアプリケーションは、 (現在のファイルの名前などの) データを各子ウィンドウに関連付けなければなりません。子ウィンドウにデータを関連付けるには、 次の2つの方法があります。

・ 子ウィンドウのデータをウィンドウ構造体に格納する方法

・ ウィンドウ プロパティを使う方法

 

 

ウィンドウ構造体のデータ

MDIアプリケーションは、 ウィンドウ クラスを登録するとき、 特定のウィンドウ クラスに固有なアプリケーション データのための追加領域をウィンドウ構造体内に予約できます。この追加領域にデータを格納したり、 追加領域のデータを取得するには、 GetWindowWord関数、 SetWindowWord関数、 GetWindowLong関数、 SetWindowLong関数を使います。

子ウィンドウのデータが大量にあるときは、 データ構造体のメモリを割り当てて、 構造体を含むメモリのハンドルを子ウィンドウの追加領域に格納してください。

ウィンドウ プロパティ

また、 ドキュメントごとのデータは、 ウィンドウ プロパティを使って格納することもできます (ドキュメントごとのデータとは、 特定の子ウィンドウのドキュメントの種類に固有なデータです)。プロパティはウィンドウ構造体の追加領域とは異なり、 ウィンドウ クラスを登録するときに追加領域を割り当てる必要はありません。また、 ウィンドウ構造体では追加領域をアクセスするのにオフセットを使いますが、 プロパティは文字列名で参照します。ウィンドウ プロパティについて詳しくは、 ウィンドウ プロパティの概要を参照してください。

マルチ ドキュメント インターフェイスの使用

次からのトピックは、 以下に示す処理の実行方法を説明しています。トピックをクリックすると、 そのトピックの内容を表示できます。

子ウィンドウやフレーム ウィンドウのクラスの登録

フレーム ウィンドウと子ウィンドウの作成

メイン メッセージ ループの作成

フレーム ウィンドウ プロシージャの作成

子ウィンドウ プロシージャの作成

子ウィンドウの作成

処理をわかりやすく説明するために、 トピックには、 典型的なMDIアプリケーションであるMultipadの例が含まれています。

 

 

子ウィンドウやフレーム ウィンドウのクラスの登録

通常のMDIアプリケーションは、 フレーム ウィンドウ用と子ウィンドウ用の2つのウィンドウ クラスを登録しなければなりません。(表やグラフなど) 複数の種類のドキュメントをサポートするアプリケーションは、 各種類についてウィンドウ クラスを登録しなければなりません。

フレーム ウィンドウのクラス構造体は、 非MDIアプリケーションのメイン ウィンドウのクラス構造体に似ています。MDI子ウィンドウのクラス構造体は、 非MDIアプリケーションの子ウィンドウのクラス構造体とは次に示す点で異なっています。

・ MDI子ウィンドウは通常のアプリケーションと同様にアイコン化できるため、 クラス構造体にはアイコンが含まれます。

・ MDI子ウィンドウには専用のメニューはないため、 メニュー名はNULLです。

・ ウィンドウ構造体に追加領域を予約します。アプリケーションは、 この領域を使って、 ファイル名などのデータを特定の子ウィンドウに関連付けることができます。

 

次のコード例は、 Multipadがフレーム ウィンドウと子ウィンドウのクラスを登録する方法を示しています。 

BOOL APIENTRY InitializeApplication()
{
WNDCLASS wc;

/* Register the frame window class. */

wc.style = 0;
wc.lpfnWndProc = (WNDPROC) MPFrameWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(hInst, IDMULTIPAD);
wc.hCursor = LoadCursor((HANDLE) NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1);
wc.lpszMenuName = IDMULTIPAD;
wc.lpszClassName = szFrame;

if (!RegisterClass (&wc) )
return FALSE;

/* Register the MDI child window class. */

wc.lpfnWndProc = (WNDPROC) MPMDIChildWndProc;
wc.hIcon = LoadIcon(hInst, IDNOTE);
wc.lpszMenuName = (LPCTSTR) NULL;
wc.cbWndExtra = CBWNDEXTRA;
wc.lpszClassName = szChild;

if (!RegisterClass(&wc))
return FALSE;

return TRUE;
}

フレーム ウィンドウと子ウィンドウの作成

MDIアプリケーションは、 ウィンドウ クラスを登録したら、 ウィンドウを作成します。まず、 CreateWindow関数かCreateWindowEx関数を使って、 フレーム ウィンドウを作成します。フレーム ウィンドウを作成したら、 もう一度CreateWindowCreateWindowExを使って、 クライアント ウィンドウを作成します。クライアント ウィンドウのクラス名には、 MDICLIENTを指定します。MDICLIENTは、 Windowsの登録済みウィンドウ クラスです。CreateWindowCreateWindowExlpvParamパラメータには、 CLIENTCREATESTRUCT構造体を指すポインタを指定してください。この構造体には、 次の表に示すメンバがあります。

メンバ 説明

hWindowMenu MDI子ウィンドウの制御に使われる[ウィンドウ]メニューを識別します。子ウィンドウを作成すると、 そのタイトルが[ウィンドウ]メニューにメニュー項目として追加されます。ユーザーは、 [ウィンドウ]メニューのタイトルを選択して子ウィンドウをアクティブ化できます。

idFirstChild 最初のMDI子ウィンドウの識別子を示します。最初に作成されたMDI子ウィンドウには、 この識別子が割り当てられます。その後のウィンドウには、 順番に1ずつ増やした識別子が割り当てられます。子ウィンドウを破棄するとき、 Windowsは、 ウィンドウ識別子をすぐに再割り当てして、 識別子が連続した値になるようにします。

Windowsは、 子ウィンドウのタイトルを[ウィンドウ]メニューに追加するとき、 子ウィンドウに識別子を割り当てます。子ウィンドウのタイトルを示すメニュー項目をユーザーが選択すると、 フレーム ウィンドウは、 wParamパラメータにその識別子が設定されたWM_COMMANDメッセージを受け取ります。idFirstChildメンバには、 フレーム ウィンドウのメニューのメニュー項目識別子と重複しない値を指定してください。

Multipadのフレーム ウィンドウ プロシージャは、 WM_CREATEメッセージを処理するときに、 MDIクライアント ウィンドウを作成します。次の例は、 クライアント ウィンドウの作成方法を示しています。 

.
. /* Process other messages here. */
.
case WM_CREATE:
{
CLIENTCREATESTRUCT ccs;

/*
* Retrieve the handle of the Window menu and assign the
* first child window identifier.
*/

ccs.hWindowMenu = GetSubMenu(GetMenu(hwnd), WINDOWMENU);
ccs.idFirstChild = IDM_WINDOWCHILD;

/* Create the MDI client window. */

hwndMDIClient = CreateWindow( "MDICLIENT", (LPCTSTR) NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,
0, 0, 0, 0, hwnd, (HMENU) 0xCAC, hInst, (LPSTR) &ccs);

ShowWindow(hwndMDIClient, SW_SHOW);

}
break;
}
.
. /* Process other messages here. */
.

子ウィンドウのタイトルは、 [ウィンドウ]メニューの最後に追加されます。アプリケーションがAppendMenu関数を使って[ウィンドウ]メニューに文字列を追加すると、 [ウィンドウ]メニューが再描画されるとき、 その文字列は子ウィンドウのタイトルを示す文字列で上書きされます (子ウィンドウが作成されたり破棄されると、 [ウィンドウ]メニューが再描画されます)。[ウィンドウ]メニューに文字列を追加するには、 InsertMenu関数を使って、 追加した文字列が子ウィンドウのタイトルで上書きされないようにしてください。

MDIクライアント ウィンドウが子ウィンドウに上書きしないようにするには、 WS_CLIPCHILDRENスタイルを使ってクライアント ウィンドウを作成してください。

メイン メッセージ ループの作成

MDIアプリケーションのメイン メッセージ ループは、 アクセラレータ キー (アクセス キー) を処理する非MDIアプリケーションのメッセージ ループに似ています。ただし、 MDIのメッセージ ループは、 アプリケーション定義のアクセラレータ キーを調べたりメッセージをディスパッチする前に、 TranslateMDISysAccel関数を呼び出します。

次の例は、 典型的なMDIアプリケーションのメッセージ ループを示しています。

 

while (GetMessage(&msg, (HWND) 
				NULL, 0, 0)) {
if (!TranslateMDISysAccel(hwndMDIClient, &msg) &&
!TranslateAccelerator(hwndFrame, hAccel, &msg)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

TranslateMDISysAccel関数は、 WM_KEYDOWNメッセージをWM_SYSCOMMANDメッセージに変換し、 アクティブなMDI子ウィンドウに送ります。メッセージがMDIアクセラレータ メッセージでなければ、 この関数はFALSEを返します。この場合、 アプリケーションは、 TranslateAccelerator関数を使って、 アプリケーション定義のアクセラレータ キーが押されたかどうか調べます。アクセラレータ キーが押されていなければ、 適切なウィンドウ プロシージャにメッセージをディスパッチします。

 

 

 

フレーム ウィンドウ プロシージャの作成

MDIフレーム ウィンドウのウィンドウ プロシージャは、 非MDIアプリケーションのメイン ウィンドウのウィンドウ プロシージャに似ています。ただし、 フレーム ウィンドウ プロシージャは、 処理しなかったメッセージをすべてDefWindowProc関数ではなくDefFrameProc関数に渡します。さらに、 一部のメッセージについては、 フレーム ウィンドウ プロシージャは、 メッセージを処理した場合でもそのメッセージをデフォルト プロシージャに渡さなければなりません。そのようなメッセージを次の表に示します。

 

メッセージ 対応

  • WM_COMMAND ユーザーが選択したMDI子ウィンドウをアクティブ化します。このメッセージは、 ユーザーがMDIフレーム ウィンドウの[ウィンドウ]メニューからMDI子ウィンドウのタイトルを選択したときに送られます。このメッセージで送られるウィンドウ識別子は、 アクティブ化するMDI子ウィンドウを識別します。
  • WM_MENUCHAR ユーザーがAlt+-(マイナス) キーを押したときに、 アクティブなMDI子ウィンドウのコントロール メニューをオープンします。
  • WM_SETFOCUS MDIクライアント ウィンドウにキーボード フォーカスを設定します。クライアント ウィンドウは、 キーボード フォーカスをアクティブなMDI子ウィンドウに渡します。
  • WM_SIZE MDIクライアント ウィンドウがフレーム ウィンドウの新しいクライアント領域に収まるようにクライアント ウィンドウのサイズを変更します。フレーム ウィンドウ プロシージャは、 MDIクライアント ウィンドウのサイズを独自に変更する場合、 このメッセージをDefWindowProc関数に渡さないでください。

Multipadのフレーム ウィンドウ プロシージャの名前はMPFrameWndProcです。MPFrameWndProcでの上記以外のメッセージの処理は、 非MDIアプリケーションでのメッセージの処理に似ています。Multipadでは、 ローカルに定義したCommandHandler関数でWM_COMMANDメッセージを処理します。Multipadが処理しないコマンド メッセージについては、 CommandHandlerはDefFrameProc関数を呼び出します。MultipadがデフォルトでDefFrameProcを使っていなければ、 [ウィンドウ]メニューの項目を選択したときに送られるWM_COMMANDメッセージの行き先がなくなるため、 ユーザーは[ウィンドウ]メニューで子ウィンドウをアクティブ化できなくなります。

子ウィンドウ プロシージャの作成

MDI子ウィンドウ プロシージャは、 フレーム ウィンドウ プロシージャと同様に、 メッセージのデフォルト処理に特別な関数を使います。子ウィンドウ プロシージャが処理しないメッセージは、 DefWindowProc関数ではなくDefMDIChildProc関数に渡してください。さらに、 一部のウィンドウ管理メッセージは、 アプリケーションが処理した場合でも、 DefMDIChildProcに渡してください。アプリケーションがDefMDIChildProcに渡さなければならないメッセージを次に示します。

メッセージ 応答

  • WM_CHILDACTIVATE MDI子ウィンドウのサイズ変更、 移動、 表示のときのアクティブ化処理を行います。このメッセージは必ず渡してください。
  • WM_GETMINMAXINFO MDIクライアント ウィンドウの現在のサイズに基づいて、 最大化したMDI子ウィンドウのサイズを計算します。
  • WM_MENUCHAR メッセージをMDIフレーム ウィンドウに渡します。
  • WM_MOVE MDIクライアント スクロール バーがあれば再計算します。
  • WM_SETFOCUS アクティブでないMDI子ウィンドウをアクティブ化します。
  • WM_SIZE MDI子ウィンドウを最大化したり復元するときなど、 ウィンドウのサイズ変更に必要な処理を行います。このメッセージをDefMDIChildProc関数に渡さないと、 大きな問題が発生します。
  • WM_SYSCOMMAND コントロール メニューのコマンドを処理します。コントロール メニューのコマンドには、 SC_NEXTWINDOWSC_PREVWINDOW SC_MOVESC_SIZE SC_MAXIMIZEがあります。

子ウィンドウの作成

MDI子ウィンドウを作成するには、 CreateMDIWindow関数を呼び出すか、 MDIクライアント ウィンドウにWM_MDICREATEメッセージを送ります (CreateWindow関数を使ってMDI子ウィンドウを作成することはできません)。単一スレッドMDIアプリケーションは、 どちらの方法でも子ウィンドウを作成できます。マルチスレッドMDIアプリケーションのスレッドが子ウィンドウを別のスレッドで作成するには、 CreateMDIWindow関数を使わなければなりません。

WM_MDICREATEメッセージのlParamパラメータは、 MDICREATESTRUCT構造体を指すポインタです。この構造体には、 4つの寸法メンバがあります。xyはウィンドウの水平位置と垂直位置を示し、 cxcyはウィンドウの水平範囲と垂直範囲を示します。これらのメンバはアプリケーションが明示的に設定できますが、 CW_USEDEFAULTを設定すると、 Windowsがカスケード アルゴリズムに従って位置やサイズを選択してくれます。どちらの場合も、 4つのメンバすべてを初期化しなければなりません。Multipadでは、 すべての寸法にCW_USEDEFAULTを使っています。

MDICREATESTRUCT構造体の最後のメンバは、 styleメンバです。このメンバは、 ウィンドウのスタイル ビットを示します。可能なウィンドウ スタイルをすべて持つMDI子ウィンドウを作成するには、 MDIS_ALLCHILDSTYLESウィンドウ スタイルを指定してください。このスタイルを指定しなければ、 MDI子ウィンドウには、 デフォルト設定としてWS_MINIMIZEWS_MAXIMIZE WS_HSCROLLWS_VSCROLLが設定されます。

Multipadは、 ローカルに定義したAddFile関数 (ソース ファイルMPFILE.Cにあります) を使ってMDI子ウィンドウを作成します。AddFile関数は、 ウィンドウのMDICREATESTRUCT構造体のszTitleメンバに編集するファイルの名前または“Untitled”を設定することによって、 子ウィンドウのタイトルを設定します。szClassメンバには、 MultipadのInitializeApplication関数で登録したMDI子ウィンドウ クラスの名前を設定します。hOwnerメンバには、 アプリケーションのインスタンス ハンドルを設定します。

次のコード例は、 MultipadのAddFile関数を示しています。 

HWND APIENTRY AddFile(pName)
CHAR * pName;
{
HWND hwnd;
CHAR sz[160];
MDICREATESTRUCT mcs;

if (!pName) {

/*
* If the pName parameter is NULL, load the "Untitled"
* string from the STRINGTABLE resource and set the szTitle
* member of MDICREATESTRUCT.
*/

LoadString(hInst, IDS_UNTITLED, sz, sizeof(sz));
mcs.szTitle = (LPCTSTR) sz;
}
else

/*
* Title the window with the full path and filename,
* obtained by calling the OpenFile function with the
* OF_PARSE flag, which is called before AddFile().
*/

mcs.szTitle = of.szPathName;

mcs.szClass = szChild;
mcs.hOwner = hInst;

/* Use the default size for the child window. */

mcs.x = mcs.cx = CW_USEDEFAULT;
mcs.y = mcs.cy = CW_USEDEFAULT;

/*
* Give the child window the default style. The styleDefault
* variable is defined in MULTIPAD.C.
*/

mcs.style = styleDefault;

/* Tell the MDI client window to create the child window. */

hwnd = (HWND) SendMessage (hwndMDIClient, WM_MDICREATE, 0,
(LONG) (LPMDICREATESTRUCT) &mcs);

/*
* If the file is found, read its contents into the child
* window's client area.
*/

if (pName) {
if (!LoadFile(hwnd, pName)) {

/* Cannot load the file; close the window. */

SendMessage(hwndMDIClient, WM_MDIDESTROY,
(DWORD) hwnd, 0L);
}
}
return hwnd;
}

WM_MDICREATEメッセージのlParamパラメータで渡したポインタは、 CreateWindow関数に渡され、 子ウィンドウに対するWM_CREATEメッセージで渡されるCREATESTRUCT構造体の最初のメンバに設定されます。Multipadの子ウィンドウは、 WM_CREATEメッセージを処理するときに、 追加データのドキュメント変数を初期化し、 エディット コントロールの子ウィンドウを作成することによって自分自身を初期化します。

マルチ ドキュメント インターフェイス関数とメッセージ

MDIアプリケーションで使われる関数とメッセージを次に示します。

 

関数

  • CreateMDIWindow
  • DefFrameProc
  • DefMDIChildProc
  • TranslateMDISysAccel

 

メッセージ

  • WM_MDIACTIVATE
  • WM_MDICASCADE
  • WM_MDICREATE
  • WM_MDIDESTROY
  • WM_MDIGETACTIVE
  • WM_MDIICONARRANGE
  • WM_MDIMAXIMIZE
  • WM_MDINEXT
  • WM_MDIREFRESHMENU
  • WM_MDIRESTORE
  • WM_MDISETMENU
  • WM_MDITILE

▲ページトップに戻る

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