EternalWindows
基礎 / メッセージのパラメータ

ウインドウプロシ−ジャにメッセージが送られた場合は、何らかのイベントが発生したことを意味します。 たとえば、WM_LBUTTONDOWNが送られたならば、ウインドウ上でマウスの左ボタンが押されたことを意味しますが、 これだけでは情報が不足しているともいえます。 たとえば、WM_LBUTTONDOWNで図形を描画しようと考えた場合、 クリックした位置が分からなければ描画位置を特定できないことになりますから、 こうしたパラメータ(補足情報)についてもウインドウプロシージャから参照したいものです。 実はウインドウプロシージャの引数には、メッセージ固有のパラメータを格納するWPARAMとLPARAMが存在するため、 これらにどのような値が格納されているのかを理解すれば、メッセージを処理する際に活かせることができます。

今回のプログラムは、Enterキーが押されるとメッセージボックスを表示します。 また、ウインドウ上でマウスの左ボタンが押されると、そのときのカーソルの位置をメッセージボックスで表示します。

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {

	case WM_KEYDOWN:
		if (wParam == VK_RETURN)
			MessageBox(NULL, TEXT("Enterキーが押されました。"), TEXT("OK"), MB_OK);
		return 0;
	
	case WM_LBUTTONDOWN: {
		int   x, y;
		TCHAR szBuf[256];
		
		x = LOWORD(lParam);
		y = HIWORD(lParam);

		wsprintf(szBuf, TEXT("X %d : Y %d"), x, y);
		MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);

		return 0;
	}

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

ウインドウがアクティブである場合にキーボードのキーが押されると、 WM_KEYDOWNというメッセージがウインドウに送られることになります。 しかし、これだけではどのキーが押されたのかが分かりませんから、 メッセージのパラメータを確認することになります。

case WM_KEYDOWN:
	if (wParam == VK_RETURN)
		MessageBox(NULL, TEXT("Enterキーが押されました。"), TEXT("OK"), MB_OK);
	return 0;

パラメータにはwParamとlParamの2種類がありますが、 押されたキーを特定したい場合にはwParamを確認します。 ここには、押されたキーを表す仮想キーコードが格納されており、 たとえば値がVK_RETURNであるならば、Enterキーが押されたことを意味します。

WM_LBUTTONDOWNでは、クリックされた位置を表示する処理を行っています。 このクリックされた位置という情報はlParamに格納されていますが、 この格納のされ方が少し複雑です。

x = LOWORD(lParam);
y = HIWORD(lParam);

lParamの型であるLPARAMはunsigned longであるため、32ビット幅です。 WM_LBUTTONDOWNではこの32ビットのうち、下位ワード(下位16ビット)をカーソルのX位置、 上位ワード(上位16ビット)をカーソルのY位置と定めています。 よって、これらを取り出すために専用のマクロを使用することになります。 LOWORDマクロに32ビット変数を指定すれば下位ワードを取得することができ、 HIWORDマクロに指定すれば上位ワードを取得することができます。 リファレンスでは、GET_X_LPARAMマクロの使用が推奨されていますが、 今回は簡単のためLOWORDとHIWORDを使用しています。

クリックされた位置は数値型としてアプリケーションに維持されていますが、 MessageBoxが要求するのはあくまで文字列型です。 よって、数値型を文字列型に整形する関数を呼び出します。

wsprintf(szBuf, TEXT("X %d : Y %d"), x, y);

wsprintfは、第2引数と第3引数の情報から文字列を整形して第1引数に返す関数です。 単純に、コンソールプログラムで用いていたsprintfのWindows版であると考えてよいでしょう。 コンソールプログラムで用いた幾つかの関数には、 それに対応するWindows版の関数があるので、 可能な限りWindows版の関数を使うようにしてください。 ちなみに、szBufという変数名のszは、この変数が文字列型であることを意味します。 Bufというのはバッファ(Buffer)の略で、特に重要でない文字列を格納する変数は、 このような名前が付けられることが多いようです。


戻る