メッセージを送る


プロシージャに送る

今まで、私たちは様々なウィンドウメッセージを受け取ってきました
しかし、それだけではなく私たちがメッセージを発信することもできます

とくに、子ウィンドウに対して何らかの操作を行う時は重要で
ウィンドウが互いにメッセージを送信し合い、情報を送るという操作ができます
それも、プログラムから明示的に子ウィンドウにメッセージを送るのです

この機能を使えば、例えばボタンをプログラムから押すことができます
プッシュボタン以外のコントロールなどではより重要になるので
今回は、このメッセージの送信について、詳しく説明しましょう

メッセージをウィンドウに送るには SendMessage() 関数を使います
LRESULT SendMessage(
	HWND hWnd , UINT Msg ,
	WPARAM wParam , LPARAM lParam
);
hWnd にはメッセージを受け取るウィンドウのハンドルを指定します
HWND_BROADCAST を指定すると、全てのトップレベルのウィンドウにメッセージを送ります

Msg は、送信するメッセージを
wParam と lParam はお馴染み、メッセージの各種パラメータです

戻り値はメッセージの処理結果で、やはりメッセージによって異なります
SendMessage() 関数の特徴は、メッセージを直接ウィンドウプロシージャに送ります
そしてウィンドウプロシージャが処理を終了するまで制御を戻りません
#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_LBUTTONDOWN:
		SendMessage(hwnd , WM_CLOSE , 0 , 0);
		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	= NULL;
	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)) DispatchMessage(&msg);
	return msg.wParam;
}
このプログラムを実行して、ウィンドウのクライアント領域を
マウスの左ボタンで押すと、ウィンドウは WM_CLOSE を自分自身に送信します
結果として、ウィンドウは閉じます

子ウィンドウにメッセージを送るときは、子ウィンドウを生成した時に
CreateWindow() 関数が返してハンドルに対してメッセージを送ります


非同期メッセージ

前の SendMessage() 関数は、直接プロシージャに対してメッセージを送りました
この場合は、プロシージャが終了するまで制御が戻りません
つまり、完全に同期がとれていると考えることができます

逆に、メッセージを送信すると即座に制御を戻すこともできます
プロシージャが処理しているか否かは関係ありません
このような処理をしたい場合は PostMessage() 関数を使います
BOOL PostMessage(
	HWND hWnd , UINT Msg ,
	WPARAM wParam , LPARAM lParam
);
hWnd には、メッセージを受け取るウィンドウのハンドルを指定します
ただし次の値を指定すると、特別な意味をもちます

定数解説
HWND_BROADCAST すべてのトップレベルウィンドウにメッセージをポストします
子ウィンドウにはポストしません
NULL dwThreadId パラメータに現在のスレッドの ID を設定して
PostThreadMessage() 関数を呼び出したかのように動作します

PostThreadMessage() については後記します

Msg には、送信するメッセージを
wParam と lParam には、それぞれメッセージの追加情報を指定します
関数が成功すると 0 以外の値が、失敗すると 0 が返ります

この関数は、メッセージを指定したウィンドウのメッセージキューにポストします
この関数で通常のメッセージを送る場合、基本的にポインタを送ってはいけません
いつ、相手のウィンドウプロシージャがメッセージを処理してくれるかわからないので
メッセージが処理される前に、ポインタが指すメモリが解放される可能性があります
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	int iWidth , iHeight;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_LBUTTONDOWN:
		PostMessage(hwnd , WM_RBUTTONDOWN , wp , lp);
		hdc = GetDC(hwnd);
		SelectObject(hdc , GetStockObject(BLACK_BRUSH));
		Rectangle(hdc , 200 , 50 , 500 , 200);
		ReleaseDC(hwnd , hdc);
		return 0;
	case WM_RBUTTONDOWN:
		hdc = GetDC(hwnd);
		for (iHeight = 0 ; iHeight < 400 ; iHeight++) 
			for (iWidth = 0 ; iWidth < 400 ; iWidth++)
				SetPixel(hdc , iWidth , iHeight , 255);
		ReleaseDC(hwnd , hdc);
		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	= NULL;
	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)) DispatchMessage(&msg);
	return msg.wParam;
}


このプログラムは PostMessage() 関数で WM_RBUTTONDOWN を送ったあと
即座に黒塗りの長方形を描画します

この動作は、WM_RBUTTONDOWN が処理される前に行われます
つまり、PostMessage() 関数がメッセージをキューにポストし
すぐに制御が戻ってきていることを証明しています

逆に、上のプログラムの PostMessage() 関数をそのまま SendMessage() 関数に
書き換えて、コンパイルして実行してみてください
黒塗りの長方形がピクセル単位で塗りつぶした赤い長方形の上にかぶさります
WM_RBUTTONDOWN の処理が終了したあとに制御が戻ることを確認できるでしょう


SendMessage()

LRESULT SendMessage(
	HWND hWnd , UINT Msg ,
	WPARAM wParam , LPARAM lParam
);
メッセージを一つ、または複数のウィンドウに送ります
指定されたウィンドウのウィンドウプロシージャを呼び出し
そのプロシージャが処理し終わった時点で制御が返されます

hWnd - メッセージを送るウィンドウのハンドルを指定します
Msg - 送信するメッセージを指定します
wParam - メッセージの情報を指定します
lParam - メッセージの情報を指定します

戻り値 - メッセージの処理の結果を受けます

PostMessage()

BOOL PostMessage(
	HWND hWnd , UINT Msg ,
	WPARAM wParam , LPARAM lParam
);
メッセージを一つ、または複数のウィンドウに関連付けられた
メッセージキューにメッセージをポストし、制御を返します

hWnd - メッセージを送るウィンドウのハンドルを指定します
Msg - 送信するメッセージを指定します
wParam - メッセージの情報を指定します
lParam - メッセージの情報を指定します

戻り値 - 成功すれば 0 以外、失敗すれば 0



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