ダイアログボックスより自由度の高いウィンドウを作ります。
本などでよく見る 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関数ですが、これ以外のメッセージも適当に処理してくれます。
これで解説は終わりです。 まあ、こんなもんだと思って下さい。 細かいことは、後回しにするのが賢明です。 大切なことは、アプリケーション名を書くことくらいです。