WisdomSoft - for your serial experiences.

ウィンドウを閉じる

ウィンドウを閉じようとしたときに発生する WM_CLOSE メッセージを処理することで、ウィンドウを閉じる直前に任意の処理を介入させられます。例えば、データ編集中にウィンドウを閉じようとしたときに、変更内容を保存して閉じるかどうかを促すダイアログを表示させることができます。

閉じるコマンドを処理する

DefWindowProc() 関数は、ウィンドウのタイトルバーにある「×」ボタンを押すなどでウィンドウを閉じると DestroyWindow() 関数でウィンドウを破棄し WM_DESTROY メッセージを発生させます。一般的なウィンドウの動作としては正しいですが、アプリケーションの状態によっては勝手にウィンドウを閉じられると困ることがあります。

標準的なアプリケーションでは、ユーザーがデータを編集している途中でウィンドウを閉じようとすると、データを保存して閉じるように促すメッセージを表示します。

図1 変更の通知
図1 変更の通知

このように、データが保存できていない状態でウィンドウを閉じようとすると、データを保存して閉じるか、保存しないで閉じるか、またはウィンドウを閉じることをキャンセルするかを選択するダイアログを表示する動作は、多くのアプリケーションで共通します。

このような処理を実現するには、ウィンドウが閉じられようとしていることをプログラムで感知する必要があります。ユーザーがタイトルバーの「×」ボタンを押すなどしてウィンドウを閉じる操作を行うと、ウィンドウプロシージャに WM_CLOSE メッセージが送られます。このメッセージを、そのまま DefWindowProc() 関数に渡すとウィンドウが破棄され、続いて WM_DESTROY メッセージが送られる仕組みになっています。

よって、カスタムのウィンドウプロシージャで WM_CLOSE メッセージを受け取ることで、ウィンドウを閉じようとしたときに独自の処理を加えることができます。WM_CLOSE メッセージを DefWindowProc() 関数に渡さなければ、ウィンドウが破棄されることも WM_DESTROY メッセージが発行されることもなく、ウィンドウを閉じる操作をキャンセルできます。

コード1
#include <windows.h>
#define WINDOWS_CLASS_NAME TEXT("WisdomSoft.Sample.Window")

LRESULT CALLBACK SampleWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg) 
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CLOSE:
	{
		int messageResult = MessageBox(
			hwnd, TEXT("ウィンドウを閉じます。\nよろしいですか?"), TEXT("確認"),
			MB_OKCANCEL | MB_ICONWARNING
		);

		if(messageResult != IDOK) return 0;
		else DefWindowProc(hwnd , uMsg , wParam , lParam);
	}
	default:
		return DefWindowProc(hwnd , uMsg , wParam , lParam);
	}

	return 0;
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	HWND window;
	WNDCLASSEX wcx;
	int returnCode = 0;

	wcx.cbSize = sizeof(WNDCLASSEX);
	wcx.style = CS_HREDRAW | CS_VREDRAW;
	wcx.lpfnWndProc = SampleWindowProc;
	wcx.cbClsExtra = 0;
	wcx.cbWndExtra = 0;
	wcx.hInstance = hInstance;
	wcx.hIcon =  NULL;
	wcx.hCursor = NULL;
	wcx.hbrBackground = (HBRUSH)COLOR_BACKGROUND + 1;
	wcx.lpszMenuName = NULL;
	wcx.lpszClassName = WINDOWS_CLASS_NAME;
	wcx.hIconSm = NULL;

	if (!RegisterClassEx(&wcx)) 
	{
		OutputDebugString(TEXT("Error: ウィンドウクラスの登録ができません。\n"));
		return 0;
	}

	window = CreateWindowEx(
		WS_EX_LEFT, WINDOWS_CLASS_NAME, TEXT("Window Title"),
		WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance ,NULL
	);
	if (!window)
	{
		OutputDebugString(TEXT("Error: ウィンドウが作成できません。\n"));
		return 0;
	}
	
	while(TRUE)
	{
		MSG msg;
		int r = GetMessage(&msg, NULL, 0, 0);
		if (r > 0) DispatchMessage(&msg);
		else if(r == -1)
		{
			OutputDebugString(TEXT("Error: メッセージの取得に失敗しました。\n"));
			break;
		}
		else
		{
			returnCode = msg.wParam;
			break;
		}
	}

	return returnCode;
}
実行結果
コード1 実行結果

コード1はウィンドウを閉じようとするとメッセージボックスが表示されます。「キャンセル」ボタンが押された場合はウィンドウを閉じる操作をキャンセルし、引き続きアプリケーションを実行します。

このプログラムのウィンドウプロシージャでは WM_CLOSE メッセージを受けるとメッセージボックスを表示させ、「OK」ボタンが押されていれば DefWindowProc() 関数に WM_CLOSE メッセージを流します。「キャンセル」ボタンが選択された場合は DefWindowProc() 関数を呼び出さないため、ウィンドウは閉じられません。