読者です 読者をやめる 読者になる 読者になる

はじめてのWin32 プロジェクト その2


このエントリーをはてなブックマークに追加

はじめてのWin32 プロジェクト - minus9dの日記 に引き続き、残りの部分を見ていく。正直よくわかっていないところが多すぎてダメすぎる。

MyRegisterClass()
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32_PROJECT));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_WIN32_PROJECT);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

この関数では、ウィンドウクラスの登録を行う。まずWNDCLASSEX構造体の変数を宣言し、このメンバに、必要な情報を入れていく。WNDCLASSEXの各メンバの意味は
WNDCLASSEX structure (Windows) に見つかった。コメント形式で意味を追記する。

	wcex.cbSize = sizeof(WNDCLASSEX);  // この構造体のサイズ。

	wcex.style			= CS_HREDRAW | CS_VREDRAW; // クラスのスタイル。
                                           // この例だと、移動やサイズ変更のために、クライアントエリアの横方向か縦方向が変化したとき、
                                           // 再描画する、という意味になる。
	wcex.lpfnWndProc	= WndProc; // ウィンドウプロシージャとして用いる関数のポインタ。
                                           // このクラスのウィンドウに送られたすべてのメッセージは、
                                           // このウィンドウプロシージャによって処理される。

	wcex.cbClsExtra		= 0; // Windows内部で管理されているクラス構造体、ウィンドウ構造体のための追加領域を予約するために使われる。
	wcex.cbWndExtra		= 0; // 同上。
	wcex.hInstance		= hInstance; // このクラスのウィンドウプロシージャを含むインスタンスのハンドル。
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32_PROJECT)); // このクラスのアイコンへのハンドル。
                                           // IDI_WIN32_PROJECT は、.rcファイルに定義されている。

	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);  // カーソルの形状を設定。
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1); // システムカラーで定義された色 COLOR_WINDOW を背景色に使用
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_WIN32_PROJECT); // メニューバーに関する定義。.rcに定義されたメニューの内容が使われる。マクロは理解できなかった…。
	wcex.lpszClassName	= szWindowClass; // 登録するクラス名
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); // このクラスの小アイコンへのハンドル。

上記で理解できなかったところを調べてみた。

  • なぜwcex.cbSizeで構造体のサイズをわざわざ設定するのか?
    • この値を見て、拡張機能が有効になるかどうかが決まるようです。
  • なぜwcex.hbrBackgroundで値を+1する必要があるのか?
    • Windows API マスターへの道
    • 「最初設計したときに、システムカラーのインデックス値を 0 から 始めてしまったために、NULL と区別付かなくなったためにそうしたらしい。 マヌケ、、、」とのことです。これはひどい…。
  • なぜwcex.hIconとwcex.hIconSmとで書式が一貫していないのか?
    • 不明。

また、返り値のATOMというのは、成功すると登録されたクラスを一意的に識別するものらしい。
参考:ウィンドウクラスの登録

InitInstance()

ウィンドウクラスを登録した後は、InitInstance()関数にてウィンドウを作成する。

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // グローバル変数にインスタンス処理を格納します。

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

CreateWindow()関数の引数の意味は以下の通り。

   hWnd = CreateWindow(
	   szWindowClass, // ウィンドウクラスの名前。
	   szTitle, // ウィンドウのメニューバーに表示される文字列。
	   WS_OVERLAPPEDWINDOW, // ウィンドウスタイルの指定。
                                // この指定はWS_OVERLAPPED | WS_CAPTION | WS_SYSMENU
                                //  | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX と同じ。
	   CW_USEDEFAULT, // ウィンドウの横方向の初期位置。この値を指定すると、
              // ウィンドウの左上端には既定位置が使用され、y パラメータは無視される。
	   0, // ウィンドウの縦方向の初期位置。
	   CW_USEDEFAULT, // ウィンドウの幅をデバイス単位で指定。この値を指定すると、
                          // 規定の幅と高さが使用される。
	   0, // ウィンドウの高さをデバイス単位で指定。
	   NULL, // 作成するウィンドウの親ウィンドウまたはオーナーウィンドウのハンドルを指定。
	   NULL, // ウィンドウスタイルに応じてメニューまたは子ウィンドウ ID を指定。
	   hInstance, // このアプリケーションの現在のインスタンスハンドル。
	   NULL // 「メッセージの lParam パラメータで渡される 構造体に入れて
                // ウィンドウに渡される値へのポインタを指定します。」とのことだが、                 // 正直よく分からない。
	   );

ShowWindow()は、画面上にウィンドウを表示する関数。

UpdateWindow()は、ウィンドウに再描画を指示する関数。

LoadAccelerators()

アクセラレータテーブルをロードし、そのハンドルを取得する関数。アクセラレータとは、WM_COMMANDを生成するキーの組み合わせ。正直これもあまりよくわかっていない。

メッセージループ

メッセージ駆動型と呼ばれるWindowsプログラミングの中枢部分。

// メイン メッセージ ループ:
while (GetMessage(&msg, NULL, 0, 0))
{
	if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

キー入力、マウス操作などの入力イベントが発生すると、Windowsはそのイベントをメッセージに変換してキューに溜める。GetMessage呼び出しは、キューからメッセージを取りだす。取り出したメッセージはMSG構造体によって取得できる。

TranslateAccelerator関数は、msg変数のメッセージがキーボードメッセージならば、hAccelTableの中に対応するエントリがあるかどうか調べる。エントリがあれば、msg.hwndに対応するウィンドウプロシージャを呼び出す。これも正直よく分かっていない。

TranslateMessage関数は、一部のキーボードメッセージを変換する。

DispatchMessage関数は、メッセージを適切なウィンドウプロシージャに転送する。