メインウィンドウ

ダイアログボックスより自由度の高いウィンドウを作ります。

よくある Windows プログラム (win125.cpp)

本などでよく見る Windowsプログラムです。

/*
	よくあるウィンドウズプログラム
*/

#include	<windows.h>
#include	<tchar.h>

// グローバル変数
HINSTANCE hInst;	// 現在のインターフェイス
HWND hWnd;			// ウィンドウハンドル

// ウインドウプロシージャ宣言
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// アプリケーション開始位置
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int nCmdShow)
{
	hInst = hInstance;	// インスタンスハンドル保存
	
	TCHAR AppName[] = TEXT("win125");	// メインウィンドウのクラス名
	TCHAR AppTitle[] = L"よくあるウィンドウズプログラム";	// タイトル
	
	MSG msg;		// MSG構造体
	WNDCLASSEX wc;	// WNDCLASSEX構造体
	
	// ウィンドウの定義
	wc.cbSize = sizeof(WNDCLASSEX);		// WNDCLASSEXのサイズ
	wc.style = CS_HREDRAW | CS_VREDRAW;	// 拡張スタイル
	wc.lpfnWndProc = WndProc;			// ウィンドウプロシージャ
	wc.cbClsExtra = 0;					// 追加領域
	wc.cbWndExtra = 0;					// 追加領域
	wc.hInstance = hInst;				// インスタンス
	wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);		// アイコン
	wc.hCursor = LoadCursor(NULL,IDC_ARROW);		// カーソル
	wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);	// 背景ブラシ
	wc.lpszMenuName = NULL;							// メニュー
	wc.lpszClassName = AppName;	// クラス名
	wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO);		// 小さなアイコン
	
	// ウインドウクラスの登録
	if(RegisterClassEx(&wc) == 0) return  0;
	
	// ウインドウの作成
	hWnd = CreateWindowEx(
			0,						// 拡張スタイル
			AppName,			// クラス名
			AppTitle,					// キャプション
			WS_OVERLAPPEDWINDOW,			// スタイル
			CW_USEDEFAULT, CW_USEDEFAULT,	// 位置
			CW_USEDEFAULT, CW_USEDEFAULT,	// サイズ
			NULL,					// 親ウィンドウのハンドル
			(HMENU)NULL,			// メニュー
			hInst,			// インスタンス
			NULL);					// ウィンドウ作成データ
	if(hWnd == NULL) return  0;
	
	// ウィンドウの表示
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);
	
	// メッセージループ
	while(GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

// ウインドウプロシージャ
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
	hWnd = hwnd;	// ウィンドウハンドル保存
	
	switch(msg){
	case WM_DESTROY:
		PostQuitMessage(0);		// メッセージループの終了
		break;
	default:
		return DefWindowProc(hWnd, msg, wp, lp);	// 既定のウィンドウプロシージャ
	}
	return 0;
}

実行画面です。

解説

WinMain関数の中が増えています。 これは、DialogBox関数のしていることを書いているのです。

ウインドウクラス登録

まず、Windows に、ウィンドウのクラスを登録します。 そのために、WNDCLASSEX構造体の変数を用意します。 この WNDCLASSEX構造体は次のように定義されています。

typedef struct {
  UINT      cbSize;
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCTSTR   lpszMenuName;
  LPCTSTR   lpszClassName;
  HICON     hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;

このメンバーに適当な値を代入します。 この値に従って、Windows はウィンドウを作ります。

	// ウィンドウの定義
	WNDCLASSEX wc;	// WNDCLASSEX構造体
	
	wc.cbSize = sizeof(WNDCLASSEX);		// WNDCLASSEXのサイズ
	wc.style = CS_HREDRAW | CS_VREDRAW;	// 拡張スタイル
	wc.lpfnWndProc = WndProc;			// ウィンドウプロシージャ
	wc.cbClsExtra = 0;				// 追加領域
	wc.cbWndExtra = 0;				// 追加領域
	wc.hInstance = hInst;			// インスタンス
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);	// アイコン
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);	// カーソル
	wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);	// 背景ブラシ
	wc.lpszMenuName = NULL;			// メニュー
	wc.lpszClassName = AppName;	// クラス名
	wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO);	// 小さなアイコン

ここでは、2つだけ説明します。

lpszClassName は、ウィンドウの名前です。 ですから、何でもいいのですが、アプリケーション名でいいようです。

必要な値を代入したら、Windows に登録します。 (Windows に教えます) これは、RegisterClassEx関数を使います。 登録に失敗すると、0 が返ってきます。

ATOM RegisterClassEx(
  CONST WNDCLASSEX *lpwcx  // クラスデータ
);
	// ウインドウクラスの登録
	if(RegisterClassEx(&wc) == 0) return  0;

ウィンドウ作成

次は、登録したウィンドウを作ります。 このために、HWND型の変数を用意します。 これに、ウィンドウのハンドルが入ります。 HWND型も見たこともない型ですが、何かの別名で、元を辿っていくと、基本的な型にたどり着くようです。 これもよく使うので、グローバル変数にしてあります。

HWND hWnd;		// ウィンドウハンドル

ウィンドウの作成は、CreateWindowEx関数を使います。 ここでも、引数に hInst や アプリケーション名を代入します。 返ってくる値は、ウィンドウを指す値です。 この値をハンドルと言います。 この後、ウィンドウを操作するとき、このハンドルを使います。 ですから、ウィンドウ=hWndと覚えておきます。 作成できない場合は NULL が返ってきます。

	// ウインドウ作成
	hWnd = CreateWindowEx(
			0,					// 拡張スタイル
			AppName,		// クラス名
			AppTitle,					// キャプション
			WS_OVERLAPPEDWINDOW,			// スタイル
			CW_USEDEFAULT, CW_USEDEFAULT,	// 位置
			CW_USEDEFAULT, CW_USEDEFAULT,	// サイズ
			NULL,					// 親ウィンドウのハンドル
			(HMENU)NULL,					// メニュー
			hInst ,		// インスタンス
			NULL);					// ウィンドウ作成データ
	if(hWnd == NULL) return  0;

キャプションとは、ウィンドウの上に表示されるタイトル文字列です。 これは、AppTitle という変数にしました。

ウィンドウ表示

ウィンドウを作っても、画面に表示されません。 表示させるには、ShowWindow関数と、UpdateWindow関数を使います。 さっそく、hWnd を使います。 また、WinMain関数で受け取った nCmdShow を引数にします。

	// ウィンドウの表示
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

これで、画面にウィンドウが表示されます。

メッセージループ

さて、ウィンドウを開くと、OS からやたらとメッセージが送られてきます。 これを受け取るための変数を用意します。

	MSG msg;		// MSG構造体

このメッセージを受け取る部分がメッセージループです。 これも訳がわかりません。

	// メッセージループ
	while(GetMessage(&msg, NULL, 0, 0))    
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

ただ、メッセージを受け取ると、ウィンドウプロシージャを呼び出します。 このループを抜ければ、アプリケーションは終了します。

ウィンドウプロシージャ

さて、メッセージを処理するウィンドウプロシージャは、WndProc です。 これは、WNDCLASSEX 構造体のメンバーに代入します。

	wc.lpfnWndProc = WndProc;			// ウィンドウプロシージャ

OS からメッセージが来るたびに、この WndProc が呼ばれることになります。 ですから、プログラムはここへ書くことになります。

// ウインドウプロシージャ
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
	hWnd = hwnd;
	
	switch(msg){
	case WM_DESTROY:
		PostQuitMessage(0);		// メッセージループを終了させる
		break;
	default:
		return DefWindowProc(hWnd, msg, wp, lp);	// 既定のウィンドウプロシージャ
	}
	return 0;
}

メッセージは、"WM_" で始まるマクロ定数を使います。 "winuser.h" の中に書いてあります。

ところで、大切なことがあります。 それは、アプリケーションを終わらせることです。

ウィンドウの "×" をクリックすると、WM_CLOSE メッセージが送られてきます。 すると、DefWindowProc関数はウィンドウを破棄します。 破棄されると、WM_DESTROY メッセージが送られてきます。 そこを捕まえて、PostQuitMessage(0) と書きます。 こうすると、メッセージループから抜け出して、きれいさっぱり終了します。

さて、DefWindowProc関数ですが、これ以外のメッセージも適当に処理してくれます。

これで解説は終わりです。 まあ、こんなもんだと思って下さい。 細かいことは、後回しにするのが賢明です。 大切なことは、アプリケーション名を書くことくらいです。


[前] [目次]