ウィンドウメッセージあれこれ

ウィンドウメッセージは数多く存在します。
ここではよく登場するウィンドウメッセージをまとめて説明します。
既に説明したメッセージもおさらいしておきます。

WM_CREATE

WM_CREATEメッセージはCreateWindow/CreateWindowEx関数でウィンドウが生成されるときに通知されます。

WPARAMは使用されません。
LPARAMCREATESTRUCT構造体のポインタが格納されています。

CREATESTRUCT構造体は以下のようになっています。

typedef struct tagCREATESTRUCTW {
 LPVOID lpCreateParams;
 HINSTANCE hInstance;
 HMENU hMenu;
 HWND hwndParent;
 int cy;
 int cx;
 int y;
 int x;
 LONG style;
 LPCWSTR lpszName;
 LPCWSTR lpszClass;
 DWORD dwExStyle;
} CREATESTRUCTW, *LPCREATESTRUCTW;

この構造体のメンバはCreateWindowEx関数の引数と全く同じで、引数に渡したものがそのまま送られてきます。
(ただし順序は逆になっています)
例えば以下のようにするとウィンドウタイトルを取得できます。


//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	CREATESTRUCT* cs;
	WCHAR* str;

	switch (message)
	{
	case WM_CREATE: //ウィンドウの生成
		cs = (CREATESTRUCT*)lParam;
		str = cs->lpszName;
		MessageBox(NULL, str, L"情報", MB_OK);

		//変数を介さず直接取得しても良い
		//str = ((CREATESTRUCT*)lParam)->lpszName;
		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

lpCreateParamsメンバ

CREATESTRUCT構造体のlpCreateParamsメンバは、CreateWindow(Ex)関数の最後の引数「LPVOID lpParam」に指定した値が格納されます。
これを利用してウィンドウプロシージャに任意の値を渡すことができます。

データ型は「LPVOID型」つまり「void*型」なので、任意のデータ型のポインタを格納できます。
(ポインタ型と同サイズの整数値を渡すことも可能です)


BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	hInst = hInstance;

	HWND hWnd = CreateWindow(
		szWindowClass, szTitle,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL,
		NULL,
		hInstance,
		L"あいうえお" //lpParam
	);

	if (!hWnd) {
		return FALSE;
	}
	ShowWindow(hWnd, nCmdShow);
	return TRUE;
}

//省略

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	WCHAR* str;

	switch (message)
	{
	case WM_CREATE:
		str = ((CREATESTRUCT*)lParam)->lpCreateParams;
		//strには"あいうえお"へのポインタが格納される

		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

ウィンドウ生成の拒否

WM_CREATEメッセージは、ウィンドウを生成するに送られてきます。
通常はウィンドウプロシージャで0を返してウィンドウを生成しますが、-1を返すとウィンドウの生成をキャンセルすることができます。
例えば条件を満たした場合だけプログラムを正常起動させたい場合などに利用できます。


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_CREATE: //ウィンドウの生成
		if (MessageBox(NULL, L"ウィンドウを作成します。", L"情報", MB_OKCANCEL) == IDCANCEL)
			return -1;

		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

このコードは、メッセージボックスで「キャンセル」を選択するとウィンドウが生成されません。
その結果CreateWindow(Ex)関数はNULLを返すので、特別な処理をしない限りプログラムは終了します。

WM_DESTROY

WM_DESTROYメッセージはウィンドウが破棄されたときに通知されます。

WPARAMLPARAMは共に使用されません。

このメッセージが通知された時点ですでにウィンドウの破棄のための処理は完了しています。
このメッセージ内で破棄を中止することはできません。

メインウィンドウではこのメッセージを受け取ったらPostQuitMessage関数でプログラムを終了させるのが一般的です。
(プログラムを終了させないことも自由です)

WM_CLOSE

WM_CLOSEメッセージはウィンドウが閉じられようとしているときに通知されます。

WPARAMLPARAMは共に使用されません。

プログラマがこのメッセージを処理しない場合、DefWindowProc関数によってDestroyWindow関数が実行されます。

BOOL DestroyWindow(
 HWND hWnd
);
ウィンドウhWndを破棄する。

DestroyWindow関数は指定のウィンドウを破棄し、WM_DESTROYメッセージを発行します。

ウィンドウ破棄の拒否

WM_CLOSEメッセージ内の処理は、(必要な処理の後に)DestroyWindow関数を実行してウィンドウを破棄するのが一般的です。
逆に言えばDestroyWindow関数を実行しなければウィンドウは閉じられません。
例えばウィンドウを閉じて良いかをユーザーに確認するダイアログを表示すると、間違えてウィンドウを閉じてしまうことを防ぐことが出来ます。


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_CLOSE: //ウィンドウを閉じる
		if (MessageBox(hWnd, 
			L"終了してもよろしいですか?",
			L"確認", MB_YESNO) == IDYES)
		{
			MessageBox(hWnd,
				L"終了します。",
				L"確認", MB_OK);
			DestroyWindow(hWnd);
		}
		else
		{
			MessageBox(hWnd,
				L"終了をキャンセルしました。",
				L"確認", MB_OK);
		}
		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

WM_QUIT

WM_QUITPostQuitMessage関数が実行されたときに通知されます。

WPARAMはPostQuitMessage関数の引数に指定された値です。
LPARAMは使用されません。

このメッセージはメッセージループでプログラム終了の判定のために使用されるもので、ウィンドウプロシージャでは処理しません。
このメッセージ取得時のWPARAMの値をプログラムの終了値(WinMain関数のreturn文)に指定するのが一般的です。

WM_PAINT

WM_PAINTメッセージはウィンドウの描画要求があったときに通知されます。

WPARAMLPARAMは共に使用されません。

このメッセージではクライアント領域の表示に必要な描画処理を行います。
基本的な使い方は文字の表示の項を参照してください。

WM_MOVE

WM_MOVEメッセージはウィンドウが移動されたときに通知されます。

WPARAMは使用されません。
LPARAMはクライアント領域のスクリーン座標(画面全体の座標)が格納されています。
(ウィンドウの座標ではない)

座標はGET_X_LPARAMGET_Y_LPARAMマクロなどで取り出すことができます。
マウス座標の項も参照してください。

このメッセージはウィンドウが移動されたに通知されます。
LPARAMの値も移動後の座標です。
このメッセージ内で移動をキャンセルすることはできません。

WM_MOVING

WM_MOVINGメッセージはウィンドウが移動されようとしているときに通知されます。

WPARAMは使用されません。
LPARAMはウィンドウの座標を表すRECT構造体のポインタです。

LPARAMのRECT構造体の値を書き換えることで、移動先を変更することができます。
例えば以下のようにすると画面からはみ出さないウィンドウを作ることができます。


//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	RECT* rt;
	static RECT rtScreen;
	switch (message)
	{
	case WM_CREATE: //ウィンドウの作成
		//画面サイズの取得
		GetWindowRect(GetDesktopWindow(), &rtScreen);
		break;

	case WM_MOVING: //ウィンドウの移動中
		rt = (RECT*)lParam;	//移動しようとしている座標(RECT構造体)
		if (rt->left < 0) { //左座標が0以下
			rt->right += 0 - rt->left;
			rt->left = 0;
		}
		if (rt->right > rtScreen.right) { //上座標が0以下
			rt->left += rtScreen.right - rt->right;
			rt->right = rtScreen.right;
		}
		if (rt->top < 0) { //右座標が画面サイズ以上
			rt->bottom += 0 - rt->top;
			rt->top = 0;
		}
		if (rt->bottom > rtScreen.bottom) { //下座標が画面サイズ以上
			rt->top += rtScreen.bottom - rt->bottom;
			rt->bottom = rtScreen.bottom;
		}
		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

GetWindowRect関数はウィンドウサイズを取得します。
GetDesktopWindow関数は画面のウィンドウハンドルを取得します。

WM_SIZE

WM_SIZEメッセージはウィンドウのサイズが変更されたときに通知されます。

WPARAMはサイズ変更の種類を表す定数です。
LPARAMはサイズ変更後のクライアント領域のサイズが格納されています。

WPARAMは以下の定数のいずれかです。

定数 説明
SIZE_RESTORED 通常のサイズ変更
最大化/最小化から元に戻った時もこの値
SIZE_MINIMIZED ウィンドウの最小化
SIZE_MAXIMIZED ウィンドウの最大化
SIZE_MAXSHOW 他のウィンドウが最大化状態から元に戻ったときに、すべてのポップアップウィンドウに送信される
SIZE_MAXHIDE 他のウィンドウが最大化されたときに、すべてのポップアップウィンドウに送信される

サイズ変更後のクライアント領域のサイズはGET_X_LPARAMGET_Y_LPARAMマクロなどで取り出すことができます。
マウス座標の項も参照してください。

このメッセージはウィンドウの生成後にも一度送られてきます。
ウィンドウやクライアント領域のサイズはこのメッセージ以外では変更されないので、GetClientRect関数などはここで実行して値を保存しておくとプログラムが少し軽くなります。

なお、ウィンドウクラスのスタイルにCS_HREDRAWおよびCS_VREDRAWを指定していると、ウィンドウの幅および高さを変更したときにWM_PAINTメッセージが送られるようになります。
(→WNDCLASS構造体)

WM_SIZING

WM_SIZINGメッセージはウィンドウのサイズが変更されようとしているときに通知されます。

WPARAMはサイズ変更されるウィンドウの端を表す定数です。
LPARAMは変更後のサイズを表すRECT構造体のポインタです。

WPARAMは以下の定数のいずれかです。

定数 説明
WMSZ_LEFT ウィンドウの左端
WMSZ_RIGHT ウィンドウの右端
WMSZ_TOP ウィンドウの上端
WMSZ_TOPLEFT ウィンドウの左上端
WMSZ_TOPRIGHT ウィンドウの右上端
WMSZ_BOTTOM ウィンドウの下端
WMSZ_BOTTOMLEFT ウィンドウの左下端
WMSZ_BOTTOMRIGHT ウィンドウの右下端

LPARAMはこれから変更しようとしているサイズ情報で、これを書き換えることで変更後のサイズを制御することができます。
例えば一定以上の大きさにはできないウィンドウを作ることができます。

WM_COMMAND

WM_COMMANDメッセージは、ウィンドウ上のコントロール(ボタンなど)を操作したときに通知されます。
詳しくはボタンコントロールで説明します。

キー/マウス入力

マウス入力に関してはマウス操作を参照してください。
キー入力に関してはキー入力を参照してください。

ユーザー定義のメッセージ

ウィンドウメッセージは、Windows API側があらかじめ定義しているもののほか、プログラマが自分で定義して使用できるものもあります。

WM_APP

WM_APPメッセージは、アプリケーション内で共通のメッセージを定義するために使用します。
具体的には「WM_APP + 1」のように、定数に数字を加算して使用します。


#define WM_MYMESSAGE1 (WM_APP + 1)
#define WM_MYMESSAGE2 (WM_APP + 2)

加算できる数値は「0~16383」の範囲です。

定義したメッセージはSendMessage関数などでウィンドウプロシージャに送信します。
受け取ったユーザー定義メッセージをどのように使用するかはプログラマの自由です。
MDI子ウィンドウの操作の項では具体的な使用例を掲載しています。

WM_USER

WM_USERメッセージは、ウィンドウクラスに固有のメッセージを定義するために使用します。
こちらも「WM_USER + 1」のようにして使用します。
加算できる数値は「0~31743」の範囲です。

こちらは主にコントロール(ボタンとかのアプリケーションの「部品」のこと)を自作する場合に使用します。
実は「WM_USER + 1」などは既存のコントロールに内部的に使用されていますが、異なるウィンドウクラスで同じ値が使用されている場合は問題ありません。
アプリケーション共通のメッセージ(WM_APPを使用すべきもの)として使用すると問題が発生する可能性があります。