LoadMenuIndirect
MFCやVCLなどのクラスを一切使わずにWin32アプリケーションを作ったことのある人なら リソースファイル(*.rc)を作ってメニューバーやダイアログなどを作成しLoadMenuなどで呼び出す、といったことをやったことがあると思います。 ヘルプでロードする関数を調べているとLoadMenuIndirectやCreateDialodIndirectというように、 末尾に「Indirect」がついた関数が目に付いたことはないでしょうか。 これらの関数はメニューバーやダイアログの構造を示すバイナリデータへのポインタを渡すとそれらを生成してハンドルを返すというものです。 LoadMenuやCreateDialodではexeファイルに組み込まれたリソースの中からお望みのバイナリデータを探してくれますが、 〜Indirectはリソースとして定義したものに限らず任意のバイナリデータを指定できる点が異なります。
違った見方をすればLoadMenuはLoadMenuIndirectを使っている、とも言えます。 実際、
と書くところを
HMENU hmenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));
と書くことで同じことができます。
HRSRC hrsrc = FindResource(NULL, MAKEINTRESOURCE(IDR_MENU1), MAKEINTRESOURCE(RT_MENU)); void *pointer = LoadResource(NULL, hrsrc); HMENU hmenu = LoadMenuIndirect((MENUTEMPLATE*)pointer);
では、LoadMenuIndirectに渡しているバイナリデータとはどんなものなのでしょう。 LoadMenuIndirectのヘルプを調べてみるとMENUTEMPLATEは、 メニューテンプレートの場合はMENUITEMTEMPLATEHEADER構造体とそれに続く一つ以上のMENUITEMTEMPLATE構造体から、 拡張メニューテンプレートの場合はMENUEX_TEMPLATE_HEADER構造体とそれに続く一つ以上のMENUEX_TEMPLATE_ITEM構造体から構成される、と書いてあります。 今回私が調べたのは拡張ではない方のメニューテンプレートです。
MENUITEMTEMPLATEHEADER構造体の宣言は以下のとおりです。
versionNumberには常にゼロを代入します。
typedef struct { // mith WORD versionNumber; // version number; must be zero WORD offset; // offset first MENUITEMTEMPLATE structure } MENUITEMTEMPLATEHEADER;
offsetはMENUITEMTEMPLATE構造体までのオフセット(何バイト先にあるか)を代入します。
MENUITEMTEMPLATE構造体は
となっています。
typedef struct { WORD mtOption; // menu item flags WORD mtID; // menu item identifier WCHAR mtString[1]; // null-terminated string for menu item } MENUITEMTEMPLATE;
mtOptionはMF_で始まる値を入力しますが、基本的に以下のものの組み合わせでいいと思います。
?が付いているものに関しては使い方がよくわかりません。これらのフラグで特に気を付けないといけないのは MF_POPUPとMF_HILITEの使い方です。MF_POPUPを持つメニューアイテムには後に続くmtIDを記述してはいけません。 「記述してはいけない」というのは「どんな値が入っていても無視される」という意味ではなくて 「mtOptionの次にあるのはmtStringである」ということです。 MF_HILITEの項目がひとつも無いとバイナリデータの終端が検出できなくなりプログラムがバグります。
MF_SEPARATOR セパレーター MF_GRAYED 灰色表示 MF_DISABLED 使用不可 MF_CHECKED チェック付き MF_USECHECKBITMAPS チェック時に専用のビットマップを使う(?) MF_BITMAP ビットマップを使う(?) MF_OWNERDRAW オーナー描画(?) MF_POPUP ポップアップ MF_MENUBARBREAK バーでブレイク MF_MENUBREAK 列でブレイク MF_HILITE ポップアップあるいはメニューの最後の項目
mtIDはそのメニューアイテムがクリックされたときにWM_COMMANDメッセージと共に送られる値で、WPARAMのLOWORDに格納されます。
mtStringはNULL終端のUnicode文字列でメニューバーに表示されます。日本語を表示する場合SJISコードではダメなので注意してください。
サンプルは大体こんな感じです。バイナリデータはWORDの配列menu_templateで実装しています。
#include <windows.h> #define IDM_OPEN 0x0101 #define IDM_SAVE 0x0102 #define IDM_QUIT 0x0103 #define IDM_FSCREEN 0x0201 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_OPEN: MessageBox(hwnd, "Open", "Test", MB_OK); break; case IDM_SAVE: MessageBox(hwnd, "Save", "Test", MB_OK); break; case IDM_QUIT: MessageBox(hwnd, "終了", "Test", MB_OK); break; case IDM_FSCREEN: MessageBox(hwnd, "全画面表示", "Test", MB_OK); break; } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow) { HWND hwnd; HMENU hmenu; WNDCLASS wc; MSG msg; // メニュー const WORD menu_template[] = { //// MENUITEMTEMPLATEHEADER //// 0, // versionNumber version number; must be zero 0, // offset offset first MENUITEMTEMPLATE structure //// MENUITEMTEMPLATE //// MF_POPUP, L'フ', L'ァ', L'イ', L'ル', L'(', L'&', L'F', L')', 0, // ファイル 0 , IDM_OPEN , L'&', L'O', L'p', L'e', L'n', 0, // Open 0 , IDM_SAVE , L'&', L'S', L'a', L'v', L'e', 0, // Save MF_SEPARATOR, 0 , 0, // セパレータ MF_HILITE , IDM_QUIT , L'終', L'了', L'(', L'&', L'Q', L')', 0, // 終了 MF_POPUP | MF_HILITE, L'表', L'示', L'(', L'&', L'V', L')', 0, // 表示 MF_HILITE , IDM_FSCREEN, L'全', L'画', L'面', L'表', L'示', 0, // 全画面表示 }; hmenu = LoadMenuIndirect(menu_template); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hThisInstance; wc.hIcon = LoadIcon(NULL, IDI_WINLOGO ); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = "Sample"; RegisterClass (&wc); hwnd = CreateWindow (wc.lpszClassName, "サンプル - LoadMenuIndirect", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME, CW_USEDEFAULT, CW_USEDEFAULT, 320, 200, NULL, hmenu, hThisInstance, NULL ); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while( GetMessage(&msg, NULL, 0, 0) > 0 ) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }