メニューメッセージ


メニューの選択を処理する

メニューは、選択状況によって登録されているウィンドウのプロシージャに
様々なメッセージを送ってきます
これを処理することで、ユーザによるメニューへの要求をかなえることができます

ユーザーがメニューをアクティブにし、項目を選択すると
そのたびに WM_MENUSELECT メッセージが送られてきます
ポップアップ上で、ユーザーがメニューを選択するたびに送られてくるため
ステータスバーなどに項目の意味を表示させるプログラムなどで使われることが多いです

WPARAM の下位ワードには選択された項目のIDが格納されています
//メニューID、またはポップアップインデックス
上位ワードには、次のいずれかのメニューフラグが格納されています

定数解説
MF_BITMAP 項目はビットマップです
MF_CHECKED 項目はチェックされています
MF_DISABLED 項目は使用禁止です
MF_GRAYED 項目は淡色表示されています
MF_MOUSESELECT 項目はマウスで選択されました
MF_OWNERDRAW 項目はオーナー描画項目です
MF_POPUP 項目はポップアップ メニューを持ちます
MF_SEPARATOR 項目はメニュー項目のセパレータです
MF_SYSMENU 項目はコントロール メニューに含まれています

LPARAM には、メニューのハンドルが格納されています
このメッセージを処理した場合 0 を返さなくてはなりません
//resource.h
#define IDM_HIZAPA 40001
#define IDM_TARUTO 40002
//リソーススクリプト
#include "resource.h"

KITTY MENU {
	POPUP "Kitty" {
		MENUITEM "Kitty on your lap" , IDM_HIZAPA
		MENUITEM "Magical nyan nyan TARUTO" , IDM_TARUTO
	}
}

STRINGTABLE {
	IDM_HIZAPA 	"拾った猫が次の日猫耳少女に!\012"
			"猫耳育成ゲームの決定版"

	IDM_TARUTO 	"魔法使いの猫耳少女たると\012"
			"彼女は自分が姫君だと思いこむが、はたして真相は…"
}
#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	TCHAR strText[1024];
	static HWND label;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		label = CreateWindow(TEXT("STATIC") , NULL ,
			WS_CHILD | WS_VISIBLE ,
			0 , 50 , 400 , 50 , hwnd , (HMENU)1 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		return 0;
	case WM_MENUSELECT:
		switch (LOWORD(wp)) {
		case IDM_HIZAPA:
			LoadString((HINSTANCE)GetWindowLong(hwnd , GWL_HINSTANCE) ,
				IDM_HIZAPA , strText , 1024
			);
			SetWindowText(label , strText);
			break;
		case IDM_TARUTO:
			LoadString((HINSTANCE)GetWindowLong(hwnd , GWL_HINSTANCE) ,
				IDM_TARUTO , strText , 1024
			);
			SetWindowText(label , strText);
			break;
		}
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= TEXT("KITTY");
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


現在選択している項目の解説をスタティックコントロールに表示するプログラムです
メニューID と文字列リソース識別子を互いに対応させています

有効なメニュー項目がクリックされるなどして選択された場合
プロシージャは WM_COMMAND メッセージを受け取ります
コントロールの場合、WM_COMMAND は通知コードやハンドルを持ちました

しかし、メニューからの WM_COMMAND メッセージはそれと異なり
WPARAM の下位ワードにメニューの ID を格納しているだけです
WPARAM の上位ワードや LPARAM の値は 0 です
一般に、LPARAM の値を調べ 0 であればメニューからの選択であると判断できます
//resource.h
#define IDM_CUT 40001
#define IDM_COPY 40002
#define IDM_PASTE 40003
#define IDM_DELETE 40004
//リソーススクリプト
#include "resource.h"

KITTY MENU {
	POPUP "編集(&E)" {
		MENUITEM "切り取り(&T)" , IDM_CUT
		MENUITEM "コピー(&C)" , IDM_COPY
		MENUITEM "貼り付け(&P)" , IDM_PASTE
		MENUITEM "削除(&L)" , IDM_DELETE
	}
}
#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND edit;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		edit = CreateWindow(TEXT("EDIT") , NULL ,
			WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
			ES_AUTOHSCROLL | ES_AUTOVSCROLL |
			ES_LEFT | ES_MULTILINE ,
			0 , 0 , 400 , 200 , hwnd , (HMENU)1 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		return 0;
	case WM_COMMAND:
		switch (LOWORD(wp)) {
		case IDM_CUT:
			SendMessage(edit , WM_CUT , 0 , 0);
			break;
		case IDM_COPY:
			SendMessage(edit , WM_COPY , 0 , 0);
			break;
		case IDM_PASTE:
			SendMessage(edit , WM_PASTE , 0 , 0);
			break;
		case IDM_DELETE:
			SendMessage(edit , WM_CLEAR , 0 , 0);
			break;
		}
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= TEXT("KITTY");
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL ,
			hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


エディットコントロールの機能をメニューでもサポートしたプログラムです
メニュー項目が選択されると、対応したメッセージがエディットコントロールに送られます

また、あまり処理することはありませんが WM_INITMENU というものもあります
これは、メニューがアクティブになろうとした時に送られてくるメッセージです
WPARAM にメインメニューへのハンドルが格納されています
//コントロールメニューがアクティブになった時でも、メインメニューのハンドルである

LPARAM は 0 です
このメッセージを処理した時は 0 を返さねばなりません

また、ポップアップが表示されようとした時は WM_INITMENUPOPUP が送られる
これは、ポップアップ内のメニューの状態を変更させる処理によく使われます

WPARAM には、ポップアップメニューのハンドルが
LPARAM の下位ワードにはポップアップインデックスが格納されています
ポップアップのインデックスは、定義された順番で 0 から数えられます

LPARAM の上位ワードがTRUEならば、ドロップダウンメニューがシステムメニューであり
FALSE ならそれ以外です。このメッセージの戻り値は 0 です

また、Alt + 文字キーを押した時に対応するメニューがない場合
WM_MENUCHAR メッセージが発生します

WPARAM の下位ワードには入力された文字コードが
WPARAM の上位ワードはアクティブなメニューを表す定数です
この値が MF_POPUP であればポップアップが MF_SYSMENU であれば
コントロールメニュー (ウィンドウメニュ、またはシステムメニュー)が表示されています

LPARAM には、メニューのハンドルが格納されています
このメッセージを処理した場合、以下の戻り値のいずれかである必要があります
以下の定数は、全て上位ワードに指定しシステムに動作を要求します

定数解説
MNC_IGNORE キャラクタをステ、短いビープ音を鳴らす
MNC_CLOSE アクティブメニューを閉じる
MNC_EXECUTE 下位ワードで指定したメニューIDで
WM_COMMAND を発生させる
MNC_SELECT 下位ワードで指定した項目を選択

これまでは、Alt + 文字キーで対応したメニューが存在しなければ
このメッセージが DefWindowProc() に渡されビープ音がならされていました
この動作を、このメッセージを処理することで変更することができます
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_MENUCHAR:
		return MNC_CLOSE << 16;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= TEXT("KITTY");
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL ,
			hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
このプログラムのリソースは、先ほどのプログラムのものと同じとします
今回は、該当するメニューがなくてもビープ音が発生することはありません



前のページへ戻る次のページへ