何の変哲もないメニューリソースです。MYMENUというのは ドキュメントウィンドウが1つもないときに出すメニューです。// mdi01.rcの一部 // 自分で作るときはwindows.hと自前のヘッダーファイルをインクルード ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "新規作成(&N)", IDM_NEW MENUITEM "終了(&X)", IDM_EXIT END END MYDOCUMENT MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "新規作成(&N)", IDM_NEW MENUITEM "閉じる(&C)", IDM_CLOSE MENUITEM SEPARATOR MENUITEM "終了(&X)", IDM_EXIT END POPUP "ウィンドウ(&W)" BEGIN MENUITEM "重ねて表示(&C)", IDM_CASCADE MENUITEM "並べて表示(&T)", IDM_TILE MENUITEM "すべて閉じる(&L)", IDM_CLOSEALL MENUITEM "アイコンの整列(&A)", IDM_ARRANGE END END
まず、WinMain関数を眺めてみると、今までと同じようでいてやはり少し違っています。 順番に違いを見ていくことにします。// mdi01.cpp #define STRICT #include <windows.h> #include "resource.h" #define IDM_FIRSTCHILD 100 LRESULT CALLBACK FrameWndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK DocProc(HWND, UINT, WPARAM, LPARAM); BOOL CALLBACK CloseAllProc(HWND, LPARAM); int MyRegisterWC(WNDPROC, LPCTSTR, HBRUSH); char szFrameClassName[] = "mdi01"; //フレームウィンドウクラス char szChildDoc[] = "document"; //ドキュメント HMENU hMenuFirst, hMenuDoc; HMENU hMenuFirstWnd, hMenuDocWnd; HINSTANCE hInst; int doc_no; int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; HWND hFrame, hClient; hInst = hCurInst; if(!MyRegisterWC(FrameWndProc, szFrameClassName,(HBRUSH)(COLOR_APPWORKSPACE + 1))) return FALSE; if(!MyRegisterWC(DocProc, szChildDoc, (HBRUSH)GetStockObject(WHITE_BRUSH))) return FALSE; hMenuFirst = LoadMenu(hInst, "MYMENU"); hMenuFirstWnd = GetSubMenu(hMenuFirst, 0); hMenuDoc = LoadMenu(hInst, "MYDOCUMENT"); hMenuDocWnd = GetSubMenu(hMenuDoc, 1); hFrame = CreateWindow( szFrameClassName, "猫でもわかるMDI", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, hMenuFirst, hInst, NULL); hClient = GetWindow(hFrame, GW_CHILD); ShowWindow(hFrame, nCmdShow); UpdateWindow(hFrame); while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateMDISysAccel(hClient, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } DestroyMenu(hMenuDoc); return msg.wParam; }
HACCEL hAccel;を定義して自前のアクセラレータを 付けることももちろん可能です。しかしMDIプログラミングでは自動的に メニューにいくつかのアクセラレータが付きます。 ドキュメントウィンドウのシステムメニューボタンを押すと 「閉じる Ctrl+F4」「次のウィンドウ Ctrl+F6」というのが自動的に 作られていることがわかります。(ただし、どれほどのユーザーが アクセラレーター・キーについて理解しているでしょうか?プログラマが あれこれ苦労していろいろな機能を付けても大部分のユーザーは使ってくれません!)
次にウィンドウクラスを定義するのは同じです。第81章 の注意を参照してください。このとき、フレームウィンドウクラスとドキュメント ウィンドウクラスのうちメンバの異なるものを引数にして、MyRegisterWCという関数 を作ってみました。それぞれアイコンも異なるものを作る場合は、これも引数に してください。(その他、もっと工夫したものを作ってみてください)それと 少し説明がいるのは「COLOR_APPWORKSPACE + 1」だと思います。VC++5.0のヘルプで WNDCLASS(WNDCLASSEX)を検索すると次のように書いてあります。
A color value must be one of the following standard system colors (the value 1 must be added to the chosen color)
これで、おわかりいただけたでしょうか。
さて、次にメニューをロードしておきます。メニューをロードするのは このタイミングでないとまずいです。LoadMenu関数などについては 第44章を参照してください。そして、必ず GetSubMenuも呼んでおきます。
左の図を見てください。「アイコンの整列」メニュー項目の 下に3つほど自動的にメニューが増えています。 この「自動的に」増えるメニュー項目の位置を示します。
hMenuDocWnd = GetSubMenu(hMenuDoc, 1);
となっているので、これは2番目の位置に増やすという意味です。 これを0にすると左の追加メニューは「ファイル」の下に追加されます。 また、
hMenuFirstWnd = GetSubMenu(hMenuFirst, 0);
は、まだドキュメントウィンドウがないときなので2番目の引数は 0としておきます。これが結構わかりにくいところです。
次に親ウィンドウであるフレームウィンドウを作ります。 このときウィンドウスタイルにWS_CLIPCHILDRENを加えておきます。 これは、「親ウィンドウの内部を描画するとき子供がいるところを 描画しない」というスタイルです。ただし、この章のサンプルでは 省略しても特に問題は生じません。
CreateWindow関数でフレームウィンドウを作った後、クライアントウィンドウ のハンドルを取得しています(hClient = GetWindow(hFrame, GW_CHILD);)。
関係ないですが、筆者は以前これで大失敗をしたことがあります。 あるウィンドウを作ったあと、すぐにXという動作をさせたいと思いました。 また、このウィンドウのプロシージャはプログラマから見えないので、 まずサブクラス化をしてそのプロシージャの中でWM_CREATEメッセージ を待ちかまえて、動作Xをさせようと思いました。しかし、いつまで待っても WM_CREATEメッセージは来ませんでした。これ当たり前なんですね。 あるウィンドウをサブクラス化する場合、このウィンドウは完全に できてしまっています。すでにWM_CREATEメッセージは処理されているわけです。 ですから、サブクラス化したプロシージャにはいつまで待っても WM_CREATEは来ないわけです。(こんな間違いをするのは筆者だけでしょう・・)
次にフレームウィンドウを最新化してメッセージループに入ります。
ここもワンパターンの書き方です。また、自前のアクセラレータも付けた人は
というように書き換えてください。while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateMDISysAccel(hClient, &msg) && !TranslateAccelerator(hwndFrame, hAccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
多分この関数は上のような使われ方しかしません。BOOL TranslateMDISysAccel( HWND hWndClient, // クライアントウィンドウのハンドル LPMSG lpMsg // メッセージデータのアドレス
今回は少し、細々と書きすぎたのでこの章はここまでとします。
次章以降プロシージャの説明です。
[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]
Update Oct/22/1997 By Y.Kumei