メインウィンドウ

Win32API関数を使った、Windowsプログラムです。 最初は、メインウィンドウを作るプログラムです。

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

本などでよく見る Windowsプログラムです。 これは長いですが、ほとんど変わらないので、雛形として使えます。 ですから、これを、そのままコピペし、AppName にアプリケーション名を代入し、 後はコードを追加していくことになります。

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

#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[] = L"win111";	// メインウィンドウのクラス名
	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;
}

実行画面です。

このプログラムを書くと、上の図のようなメインウィンドウができます。 すると、OS から頻繁にメッセージがやって来ます。 ですから、そのメッセージに応えるようにプログラムを書くことになります。

解説

深入りせず、さらっと見ていきます。

アプリケーション開始位置

まず、WinMain関数です。 これは、Win32API 関数です。 アプリケーションはここからスタートします。 C/C++ の main 関数に相当します。 そして、{   } の中にプログラムを書いていきます。 WinMain関数を抜ければアプリケーションは終了します。

#include    <windows.h>

// グローバル変数
HINSTANCE hInst;	// 現在のインターフェイス

// アプリケーション開始位置
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
{
	hInst = hInstance;	// インスタンスハンドル保存
}

Windows のプログラムでは、HINSTANCE のような変な型が出てきます。 しかも沢山あります。 しかし、別名であって、元を辿れば int のような基本的な型になります。 ですから、コンパイルエラーになったとき、本来の型がわかります。

また、Unicode という文字コードがあります。 それに対応した書き方もあります。 これも別名になっているだけで、上と同じです。

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

// グローバル変数
HINSTANCE hInst;	// 現在のインターフェイス

// アプリケーション開始位置
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int nCmdShow)
{
	hInst = hInstance;	// インスタンスハンドル保存
}

さて、どこかから、4つの変数が送られてきます。 2つは受け取ります。 hInstance は、このアプリケーションに割り当てられた値です。 Windows では、複数のアプリケーションが動いています。 それらを区別するためです。 これは、プログラムでも使うのでグローバル変数に保存しておきます。 次に、カッコの中を見ていきます。

ウインドウクラス登録

まず、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 が返ってきます。

	// ウインドウクラスの登録
	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関数ですが、これ以外のメッセージも適当に処理してくれます。

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

データ型

Windowsプログラムでは、不思議なデータ型が出てきます。 しかし、C/C++の型の別名です。 ただ、Win32 API 関数を使うときは不思議な型を使うべきでしょう。

数値

C/C++ では、int は何ビットか固定されていません。 Win32API 関数の方は固定した作りになっています。 そのギャップを埋めるために工夫してあります。 表にすると次のようになります。

内容
CHARchar
INTsigned int
FLOATfloat
VOIDvoid
LPCHAR, PCHARchar*

これ以外にも沢山あります。 ただ、Win32API関数の定義に従うだけですから、覚える必要はないです。 また、"LPCHAR" のように、"LP" を付けるとポインタになります。 "P" を付けてもポインタになります。 ただ、どちらも同じ内容です。 これは他の場合もそうなっています。

文字列

文字列の方は複雑です。 文字コードの変更があったからです。 古い Windows95 では、マルチバイト文字、新しい Windows では、Unicode となりました。 そして、C/C++ では、コードを書く段階でどちらかにしなければならないのです。 それでは不便ということで、マクロが用意されています。 次の表は、UNICODEマクロが定義されている場合は、Unicode 対応となり、 そうでなければ、マルチバイト文字対応となります。

Unicodeマルチバイト文字
TCHARWCHARCHAR
LPTSTRLPWSTRLPSTR
LPCTSTRLPCWSTRLPCSTR

また、"Hello" はマルチバイト文字、L"Hello" は Unicode です。 これは、TEXT マクロを使います。 TEXT("Hello") は、UNICODEマクロが定義されていれば、L"Hello" 、そうでなければ "Hello" と置き換えてくれるのです。

文字列関数

文字列を扱う関数も影響を受けます。 strcat はマルチバイト文字、lstrcat は Unicode です。 "l" が付くだけです。

ただ、"よくあるウィンドウズプログラム" では Unicode で書いています。


[目次] [次]