EternalWindows
リージョン / ウインドウリージョン

これまで、リージョンを描画に使用したり、クリッピングリージョンに設定したりしてきましたが、 リージョンはウインドウにも設定することができます。 次に示すSetWindowRgnを呼び出した場合、ウインドウの形状は指定されたリージョンに変化します。

int SetWindowRgn(
  HWND hWnd,
  HRGN hRgn,
  BOOL bRedraw
);

hWndは、ウインドウハンドルを指定します。 hRgnは、リージョンのハンドルを指定します。 bRedrawは、ウインドウにリージョンを設定した後、再描画を行うかどうかを指定します。 FALSEを指定した場合は、再描画されません。 SetWindowRgnが0以外の値を返して成功した場合は、 第2引数のリージョンがシステムによって所有されるため、 これ以降リージョンを必要とする関数を呼ぶことはできません。

今回のプログラムは、楕円型のリージョンをウインドウに設定します。 ウインドウを閉じる場合は、ウインドウ上でマウスの右ボタンを押します。

#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(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, szAppName, szAppName, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, 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)
{
	static HRGN hrgn = NULL;

	switch (uMsg) {

	case WM_CREATE:
		hrgn = CreateEllipticRgn(0, 0, 200, 200);
		if (hrgn == NULL)
			return -1;

		SetWindowRgn(hwnd, hrgn, TRUE);
		
		return 0;
	
	case WM_NCHITTEST: {
		LRESULT lr;
		
		lr = DefWindowProc(hwnd, uMsg, wParam, lParam);
		if (lr == HTCLIENT)
			return HTCAPTION;

		return lr;
	}

	case WM_NCRBUTTONDOWN:
		PostMessage(hwnd, WM_CLOSE, 0, 0);
		return 0;
	
	case WM_PAINT: {
		HDC         hdc;
		RECT        rc;
		PAINTSTRUCT ps;

		hdc = BeginPaint(hwnd, &ps);
		
		GetClientRect(hwnd, &rc);
		FillRect(hdc, &rc, (HBRUSH)GetStockObject(GRAY_BRUSH));

		EndPaint(hwnd, &ps);

		return 0;
	}

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

プログラムを実行すると、灰色をした楕円が表示されることになりますが、これがウインドウです。 リージョンを設定したことにより、本来のウインドウの形状は失われていますが、 灰色の部分はれっきとしたウインドウのクライアント領域です。 クライアント領域が灰色になっているのは、WM_PAINTでクライアント領域を塗りつぶしているからです。 今回は作成しているリージョンが楕円型ですが、 たとえば前節のようなビットマップの型のリージョンを作成し、 WM_PAINTでビットマップを描画するようにすれば、 ウインドウはデスクトップアクセサリのように見えることになるでしょう。

リージョンをウインドウに設定することにより、ウインドウのタイトルバーなどは隠れ、 クライアント領域のみが表示されることになりますが、 これではウインドウを移動したり閉じたりすることができなくなるのではないでしょうか。 このための対策として、今回はWM_NCHITTESTとWM_NCRBUTTONDOWNを処理しています。

case WM_NCHITTEST: {
	LRESULT lr;
	
	lr = DefWindowProc(hwnd, uMsg, wParam, lParam);
	if (lr == HTCLIENT)
		return HTCAPTION;

	return lr;
}

case WM_NCRBUTTONDOWN:
	PostMessage(hwnd, WM_CLOSE, 0, 0);
	return 0;

WM_NCHITTESTは、あらゆるマウスメッセージより先立って送られるメッセージであり、 このメッセージが返す値によって、実際の操作を表すマウスメッセージが生成されることになります。 DefWindowProcがHTCLIENTを返した場合は、クライアント領域がマウスで押下されたことを意味しますが、 ここでHTCAPTIONという本来とは異なる値を返すことにより、 タイトルバーを押下した際のメッセージを意図的に生成することができるようになります。 つまり、クライアント領域の押下をタイトルバーの押下と見立てることにより、 ウインドウを移動させるためのメッセージを生成しようとしているわけです。 WM_NCRBUTTONDOWNは、ウインドウの非クライアント領域で右ボタンが押下された場合のメッセージですが、 このメッセージが送られる理由も、先のHTCAPTIONによるものです。 タイトルバーは、ウインドウの非クライアント領域ですから、 これを押下と見なせば当然、非クライアント領域に関するメッセージも生成されるようになります。 今回は、ウインドウを閉じるためにWM_CLOSEをポストしているだけですが、 デスクトップアクセサリを開発する場合は自作のメニューを表示することになるでしょう。

今回のように、ウインドウにリージョンを設定する場合は、CreateWindowExの引数も少し変更することになります。

hwnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, szAppName, szAppName, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, NULL, NULL, hinst, NULL);

この関数の第1引数は、拡張ウインドウスタイルに関する定数を指定することができます。 WS_EX_TOPMOSTはウインドウを常に前面に表示することを意味し、 WS_EX_TOOLWINDOWはタスクバーにウインドウを表示しないことを意味します。 第4引数のWS_POPUPは、ウインドウをクライアント領域のみで構成することを意味します。 リージョンを設定する場合は、タイトルバーや枠などが不要ですから、この定数を指定することになるでしょう。 また、この定数を指定した場合は、第7引数と第8引数にウインドウの幅と高さを明示的に指定することになります。 このサイズが設定するリージョンのサイズよりも小さい場合は、 リージョンが完全に表示されなくなるので注意してください。


戻る