EternalWindows
基礎 / 基本コード

前節はメッセージボックスを表示するプログラムを作成しましたが、 メッセージボックスはユーザーに何らかのメッセージを伝えることが目的であるため、 そのプログラムの個性を出すには不十分でした。 これを解決するには、ウインドウの作成と操作に関するコードを一から書けばよいのですが、 以下のプログラムからも想像が付くように非常に大変な作業となります。

#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, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 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)
{
	switch (uMsg) {

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

このプログラムは、ウインドウを表示するために必要なコードで構成されています。 つまり、ウインドウを表示するプログラムを作るにあたっては、 最低限これだけのコードは書かなければならないということを意味しています。 はっきりいって、この時点でやる気を失ってしまう方も多いと思われますが、 まずは上記した基本コードを1つずつ解析していく前に、 Windowsプログラミングの概要というものをしっかりと押さえたいと思います。 次の図は、今回のプログラムの実行結果です。

このウインドウはメニューバーや画像などが表示されているわけでもなく、 最も典型的でシンプルなウインドウといって差し支えないと思われます。 これから私たちは、このシンプルなウインドウを表示するプログラムに 独自のコードを追加して個性あるプログラムを作ることになりますが、 一体何処にどのようなコードを追加したものでしょうか。 たとえば、このウインドウでマウスの左ボタンを押したときに メッセージボックスを表示したいと考えた場合、 少なくとも以下のようなコードを書くことになるのは間違いないでしょう。

if (左ボタンが押された) {
	MessageBox(NULL, TEXT("左ボタンが押されました。"), TEXT("OK"), MB_OK);
}

このコードは不完全ながらも、考え方としては的を得ていると思われます。 問題は、if文の条件式とこのコードを何処に書けばよいのかですが、 もう1つ意識しておかなければはならない問題が残っています。 それは、上記のようなif文は無数に存在しえるというということです。 たとえば、ウインドウの「閉じる」ボタンが押されたような場合も 先の例のようにそのタイミングを捕らえることになるでしょう。

if (「閉じる」ボタンが押された) {
	// ウインドウを破棄するコードを書く
}

確かに、このコードも不完全ながら理にかなっているように思えますが、 このようなどのプログラムにでも必要になるであろう処理までも 常に考慮しなければならいなとなると、if文の数は増える一方となってしまいます。 また、そのようなif文がコード中の様々な場所に書かれたとなっては、 コードの管理が大変難しくなるという問題も生じてくるでしょう。 このような理由などからWindowsプログラミングでは、 "ウインドウにイベントが発生すると専用の関数が呼ばれる"という イベント駆動型プログラミングの形式を採用しています。 イベントとは、先の例で述べた「ウインドウ上でマウスの左ボタンを押す」や、 「閉じるボタンを押す」といったようなユーザーの操作で発生するものを表し、 それが発生した場合に専用の関数が呼ばれることになります。 この専用の関数は特別にウインドウプロシージャと呼ばれ、 今回のプログラムのWindowProcが正にそれにあたります。

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);
}

この関数の内部については次節から取り上げますが、 少なくとも現段階で押さえておかなければならないことは、 イベントの処理は必ずこの関数に書くことになるということです。 つまり、マウスの左ボタンが押されたときの処理をWinMainに 書くようなことはまずありえません。 この点は非常に重要なので必ず理解しておいてください。

ところで、WinMainではどのような処理を行うのかですが、 こちらはウインドウの作成や表示に関するコードを書くことになります。 WinMainの内部をよく見れば、CreateWindowExやShowWindowといった、 それらしきWindows APIを呼び出していることに気付くと思われますが、 幸いにもこれらの関数を含め、WinMainの内部について理解しておく必要は全くありません。 それは、プログラムの個性というものがユーザーからのイベントに応じることであったり、 何らかの画像をウインドウに描画したりすることであったりするため、 ウインドウを作成するというコード事体は多くのプログラムで同一となり、 変更する必要もないからです。 つまり、基本的なWindowsプログラミングとは、 ウインドウプロシージャにイベント処理のコードを書くことであり、 そのコードがプログラムに個性を持たせることになるのです。


戻る