第12章 システムメニューの改造


前回までに作った簡単な時計についてもう少し見てみましょう。 何か不満な点はありませんか。いろいろ、いじってみると 次のような点が不満でした。(筆者は)

タイトルバーにあるアイコンを右クリックすると 次のようになります。

[第1点]この、アプリケーションは、タスクバー上で 時刻を刻むだけなので「元のサイズに戻す」とか、 「移動」などの項目は不要ですね。また、自分で新しい 項目を設定するにはどうしたらよいのでしょうか。

[第2点]今度は、タスクバー上の アイコンを左クリックすると小さなウィンドウがデスクトップ上に 出現してみっともない。

まず、簡単なのは第2点目です。左クリックしても ユーザーの目に入らなければよいわけですから、 ずるい方法があります。

hWnd = CreateWindow(szClassName, NULL, //タイトルバーにこの名前が表示されます WS_CAPTION | WS_SYSMENU, //ウィンドウの種類 9999, //X座標 9999, //Y座標 0, //幅 0, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL);

というように書き換えます。これで、タスクバー上の アイコンを左クリックしても、小さなウィンドウは 画面のずーっとむこうの方に行ってしまい、ユーザーには 左クリックが無効であるかのように見えます。 (これは、かなりインチキ臭い方法ですね。)

次に、第1点目の不満について解消しましょう。

これは、システムメニューを改造すればよいのです。 では、どのように行えばよいのでしょうか。

1.システムメニューのメニューハンドルを取得する。 2.システムメニューの不要な項目を消す。 3.必要な項目を追加する。 4.追加項目については、自分でメッセージ処理をする。

と、いうことになります。

では、1つずつ解説します。 システムメニューのメニューハンドルを取得するには、

HMENU GetSystemMenu( HWND hWnd, // ウィンドウ・ハンドル BOOL bRevert // リセットフラグ );

を使います。リセットフラグをFALSEにセットすると 現在使用中のシステムメニューのハンドルを返します。 ちなみに、TRUEをセットすると現在のメニューを破棄して Windowsのデフォルトメニューになります。この時戻り値は 不定です。

次に、メニューハンドルを取得したら

BOOL DeleteMenu( HMENU hMenu, //メニューハンドル UINT uPosition, //メニュー項目の位置 UINT uFlags // メニューフラグ );

で、不要な項目を削除します。最初の引数は、GetSystemMenu 関数で得たメニューハンドルです。

2番目の引数は、どのメニュー項目を消すかを指定しましす。 指定の仕方は3番目のフラグによって決まります。 たとえば、3番目のフラグがMF_BYPOSITIONならば、 1番上を0としてその位置を数値で指定できます。 3番目のフラグがMF_BYCOMMANDならば、メニュー項目識別子 (ID)を指定します。この場合、識別子がわからないときは MF_BYPOSITIONを指定するのが無難です。 次に、自前のメニュー項目を加えます。

BOOL AppendMenu( HMENU hMenu, // 変更するメニューのハンドル UINT uFlags, // メニューアイテム・フラグ UINT uIDNewItem, // メニュー項目ID LPCTSTR lpNewItem // メニュー項目の内容 );

を使います。メニューアイテムフラグは、メニュー項目の 種類を指定します。この場合メニュー項目は文字列なので MF_STRINGを指定します。3番目の引数は、新しく追加した メニュー項目のIDを指定します。この場合、一応うまく動くか どうかのテストですのでIDM_TESTというように決めました。 最後は、表示するメニュー項目の内容です。

メニューを改造したら、必ずDrawMenuBar関数を実行します。 これら一連のシステムメニュー改造プログラムは、 親ウィンドウができたらすぐに実行します。 ここでは、InitInstance関数の中で、UpdateWindow 関数直後に実行することにしました。

//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, LPCSTR szClassName, int nCmdShow) { HWND hWnd; HMENU hMenu; int i; hWnd = CreateWindow(szClassName, NULL, //タイトルバーにこの名前が表示されます WS_CAPTION | WS_SYSMENU, //ウィンドウの種類 9999, //X座標 9999, //Y座標 0, //幅 0, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, SW_MINIMIZE/*nCmdShow*/); UpdateWindow(hWnd); hMenu = GetSystemMenu(hWnd, FALSE); for (i = 0; i <= 5; i++) DeleteMenu(hMenu, 0, MF_BYPOSITION); AppendMenu(hMenu, MF_STRING, IDM_TEST, "項目の追加だよ"); DrawMenuBar(hWnd); return TRUE; }

ここで、DeleteMenuについて、補足説明をします。 まず不要な項目は、上から6つあります。(境界線もメニュー項目です) 最初にポジション0で一番上の項目を削除します。 すると、今まで2番目にあった項目が1番上に来ます。 それを消すには、やはりポジション0を消します。 従ってポジション0を消す操作を6回繰り返せばよいことになります。

さあ、これでシステムメニューは改造されました。しかし、まだ メッセージ処理部分を作っていません。システムメニューから コマンドが選択されたときはどんなメッセージが出されるのでしょうか。 答えは、WM_SYSCOMMANDメッセージです。これは、ヘルプによると

WM_SYSCOMMAND uCmdType = wParam; xPos = LOWORD(lParam); yPos = HIWORD(lParam);

となっています。もうわかりましたね。WM_SYSCOMMANDを捕まえて その中でさらに、wParamでスイッチ文を作ればよいのですね。

case WM_SYSCOMMAND: switch (wp) { case IDM_TEST: MessageBox(hWnd, (LPCSTR)"テスト項目が選ばれました。", (LPCSTR)"テスト", MB_OK); break; default: return(DefWindowProc(hWnd, msg, wp, lp)); break; } break;

と、いうような感じになります。自分で作った項目はすべて自前で処理して下さい。 それ以外はDefWindowProcに任せましょう。わからんことは すべてDEF任せ!

上の例では、デフォルトのシステムメニュー項目はすべて人任せにしましたが、 これを自前で処理することもできます。SC_何とかというメッセージが これに当たります。SCはシステムコマンドの略です。(多分ね) たとえば、SC_CLOSEを捕まえれば、「閉じる」コマンドを自分で処理する こともできます。いろいろ試してみて下さい。 これで、一応「簡単な時計」のプログラムの形ができました。 今回は、自前で作ったメニュー項目をクリックするとメッセージボックスが 出るだけですが、ここをクリックするとダイアログボックスが出て、 もっと複雑な処理ができるように工夫してみましょう。 ダイアログボックスの作り方は次章で解説します。


[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Apr/04/1997 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。