EternalWindows
ウインドウ管理 / ウインドウの表示

前節では、CreateWindowExでウインドウを作成しましたが、 一般にはウインドウを作成してもウインドウが表示されることはありません。 そのため、ウインドウを表示する関数を明示的に呼び出すことになります。 ウインドウを表示する関数はいくつか存在しますが、 ShowWindowが最もよく使われている関数であろうと思われます。

BOOL ShowWindow(
    HWND hWnd,
    int nCmdShow
);

hWndは、CreateWindowExが返したウインドウハンドルを指定します。 nCmdShowは、ウインドウをどのように表示するかを示す定数を指定します。 よく使われる定数を以下に示します。

定数 意味
SW_HIDE ウインドウを非表示にし、他のウインドウをアクティブにする。
SW_MAXIMIZE ウインドウを最大化する。
SW_SHOWMINNOACTIVE ウインドウをアクティブにせず最小化する。
SW_SHOW ウインドウをアクティブにし、現在の位置とサイズで表示する。
SW_RESTORE ウインドウをアクティブにして表示するが、 最大化や最小化しているウインドウは元のサイズに戻す。
SW_SHOWNORMAL SW_RESTOREと同じ効果を持つが、 初めてのShowWindowの呼び出しでこの定数を指定しても効果はない。

この表を見れば分かるように、たとえばSW_MAXIMIZEを指定すると、 ウインドウを最大化した状態で表示できます。 しかし、ウインドウをどのように表示するのかの決定権は、 アプリケーションではなくユーザーの意思に委ねたほうが好ましいでしょう。 実は、WinMain関数の第4引数のnCmdShowには、 先に述べた表示に関わる定数が格納されており、 それはユーザー(ときには、プロセス)の意思を反映した値が格納されます。 そのため、ShowWindowの一般的な呼び出し方は次のようになります。

ShowWindow(hwnd, nCmdShow);

ところで、ユーザーはウインドウの表示状態を何処で決めることができるのでしょうか。 代表的なものでいえば、ショートカットが挙げられます。 次の図は、エクスプローラからexeファイルのショートカットを作成し、 そのプロパティを開いたものです。

「実行時の大きさ」というコンボボックスでは、 3つの表示状態のうち1つを選ぶことができます。 それらとWinMain関数のnCmdShowとの関係は、次のようになります。

実行時の大きさ nCmdShow
通常のウインドウ SW_SHOWNORMAL
最小化 SW_SHOWMINNOACTIVE
最大化 SW_MAXIMIZE

実行時の大きさを調整できるというのは、ユーザーから見れば便利な機能ですが、 アプリケーションにとっては迷惑となることもあると思われます。 たとえば、ゲームのようなアプリケーションは一般に最大化は許可しませんから、 ウインドウを表示すると共に最大化してしまうようなことは避けるべきです。 このようなときはnCmdShowの値を確認し、ShowWindowの呼び出し方を調整することになります。

if (nCmdShow == SW_MAXIMIZE)
	ShowWindow(hwnd, SW_SHOW);
else
	ShowWindow(hwnd, nCmdShow);

このコードはnCmdShowがSW_MAXIMIZEの場合は、 nCmdShowを指定せずに(つまり、最大化を許可せず)、SW_SHOWを指定しています。 SW_SHOWはウインドウを単純に表示する定数ですが、 実際にはここでSW_SHOWを指定してもウインドウは最大化されてしまいます。 何故なら、CreateWindowExはウインドウを作成すると共に、 ウインドウの大きさをnCmdShowで指定されものに調整するからです。 たとえば、nCmdShowがSW_MAXIMIZEであるならば、 ウインドウは最大化された状態で作成されます。

CreateWindowExの立場から考えれば、たとえ引数にnCmdShowを要求しなくても、 nCmdShowと同じ値を簡単に取得できます。 というのも、nCmdShowはSTARTUPINFO構造体のwShowWindowメンバと同一の値だからです (STARTUPINFO構造体は、GetStartupInfoから取得できます)。 そもそも、SW_SHOWはウインドウを現在の大きさで表示しますから、 最大化された状態で表示されるのは当然の結果といえます。 ここで本当に行うべきだったことは、ウインドウを通常の大きさに戻すことです。

#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 & ~WS_MAXIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	if (nCmdShow == SW_MAXIMIZE)
		ShowWindow(hwnd, SW_RESTORE);
	else
		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_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

SW_RESTOREは、ウインドウを通常の大きさに戻します。 これにより、最大化されたウインドウは、 通常の大きさのウインドウとして表示されることになります。 さらに、このプログラムはウインドウスタイルにWS_MAXIMIZEBOXを含めていませんから、 ユーザーはウインドウが表示された後も最大化を行うことはできません。

ウインドウスタイルといえば、WS_VISIBLEという表示に関わる定数があります。 実は、このスタイルを付けてCreateWindowExを呼び出すと、 ウインドウは最初から表示された状態で作成されることになります。 このようなときは、ShowWindowを呼ぶ必要はありませんから、 ウインドウの表示状態がどうなるのかが気になるところです。 しかし、先に述べたようにCreateWindowExは内部でnCmdShowを考慮しているため、 ユーザーの意図した大きさで表示されることになります。 通常、WS_VISIBLEは子ウインドウを作成する際に指定します。

UpdateWindowについて

ShowWindowの後に呼ばれているUpdateWindowは、 表示したウインドウのクライアント領域を直ちに描画するために存在します。 アプリケーションは、描画コードをWM_PAINTというメッセージで記述し、 システムはこのメッセージをウインドウに対して描画が必要になった時点でポストします。 この描画が必要になる場合というのは、ShowWindowの呼び出しにも該当するため、 ShowWindowを呼び出した場合はWM_PAINTがポストされます。 つまり、UpdateWindowを呼び出さなくてもWM_PAINTは処理されることになり、 ウインドウの描画は行われることになります。

WM_PAINTがポストされるのにも関わらずUpdateWindowを呼び出す理由は、 WM_PAINTの優先順位が非常に低いためです。 ポストされたメッセージは、優先順位の高いものからGetMessageで取得されるため、 WM_PAINTが処理されるまでにはわずかの時間が掛かります。 しかし、既にウインドウが表示されている以上、 ウインドウのクライアント領域には正しい描画が行われていることを望みますから、 ウインドウの見た目はいち早く完成しておかなければなりません。 よって、ShowWindowの後にUpdateWindowを呼び出すことになります。 この関数は、ウインドウプロシージャを直接呼び出してWM_PAINTを処理するため、 関数が制御を返した時点で描画処理は既に完了しています。



戻る