EternalWindows
メッセージキュー / スレッドとメッセージボックス

前節で述べたように、1つのスレッドはメッセージキューを持っており、 そのメッセージキューにはスレッドに関連付けられているウインドウへの メッセージだけがポストされることになります。 メッセージキューがスレッド単位で存在することを証明するために、次のコードを検討します。

#include <windows.h>

DWORD WINAPI ThreadProc(LPVOID lpParameter);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR  szBuf[256];
	DWORD  dwThreadId;
	HANDLE hThread;

	SetMessageExtraInfo(10);
	
	hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0, &dwThreadId);

	wsprintf(szBuf, TEXT("%d"), GetMessageExtraInfo());
	MessageBox(NULL, szBuf, TEXT("スレッドA"), NULL);

	CloseHandle(hThread);

	return 0;	
}

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	TCHAR szBuf[256];

	wsprintf(szBuf, TEXT("%d"), GetMessageExtraInfo());
	MessageBox(NULL, szBuf, TEXT("スレッドB"), NULL);

	return 0;
}

このプログラムではWinMain関数を実行しているスレッドが第2のスレッドを作成し、 どちらもGetMessageExtraInfoの戻り値を表示しています。 GetMessageExtraInfoは、スレッドのメッセージキューに関連付けられている32ビット値を取得する関数です。 この32ビット値は、SetMessageExtraInfoで設定することができます。

実行結果ですが、2つのメッセージボックスの内容は異なっているはずです。 スレッドAはSetMessageExtraInfoを呼び出したスレッドです。 ですから、このスレッドには10という値がメッセージキューに設定されることになります。 そのため、スレッドAがGetMessageExtraInfoを呼び出すと10が返ります。 しかし、スレッドBでのGetMessageExtraInfoは0です。 理由は、スレッドBはSetMessageExtraInfoを呼び出していないからです。 これは正に、メッセージキューがスレッド単位であることを証明しています。

メッセージキューがスレッド単位であるということを理解するには、 物事を小さな部位から考えてみるとよいと思われます。 たとえば、先のプログラムではメッセージボックスを表示しているわけですが、 このメッセージボックスを閉じたり移動できたりするのは何故でしょうか。 メッセージボックスを移動すると呼び出し側のスレッドにWM_MOVINGというメッセージが送られることになります。 そして、このメッセージ通りの動作をメッセージボックスが行うということは、 メッセージボックス事体が呼び出し側スレッドのメッセージキューから、 メッセージを取得しているとしか考えようがないでしょう。 この考えは、以下のプログラムで証明できます。

#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;
	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);
	
	MessageBox(NULL, TEXT("メッセージボックス"), TEXT("OK"), MB_OK);

	return 0;
}

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

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

このプログラムは、ウインドウとメッセージボックスを表示します。 見て分かるようにメッセージループのコードが存在しないのにも関わらず ウインドウを動かせるわけですが、これは全く不思議なことではないのです。 ウインドウを動かしたとき、WM_MOVINGがウインドウを作成したスレッドに ポストされますが、このメッセージはメッセージボックスが取得してウインドウへ送信してくれるからです。 メッセージボックスは、次のようにGetMessageを呼び出していると思われます。

GetMessage(&msg, NULL, 0, 0);

第2引数がNULLであるというところに注目してください。 もし、ここにメッセージボックス自身のウインドウハンドルを取得したら、 自分へのメッセージを取得するだけだったでしょう。 NULLに指定すれば、呼び出し側スレッドのメッセージキュー内の 全てのメッセージを取得することになるので、 プログラムが作成したウインドウへのメッセージも取得されるのです。


戻る