Windows API Primer   ウィンドウ

ホーム   目次

ウィンドウ

Windows APIでウィンドウを表示するには、独自のウィンドウクラスを作成して、そのウィンドウクラスをシステム(Windows OS)に登録します。そして、その登録されたウィンドウクラスからウィンドウを作成して表示します。

  1. ウィンドウクラスを作成する。
  2. 作成したウィンドウクラスをシステム(Windows OS)に登録する。
  3. システム(Windows OS)に登録されたウィンドウクラスからウィンドウを作成して表示する。

イベントループ

前章のメッセージボックスは、メッセージボックスがアプリケーションの進行を止めますので、メッセージボックスを閉じるまでは、アプリケーションは起動し続けていました。

しかし通常のアプリケーションは、イベントループなどと呼ばれる無限ループを作成して強制的に起動し続けなければ、そのアプリケーションは一瞬起動するだけで、すぐに終了してしまいます。

このような無限ループをプログラミング用語ではイベントループやメインループと言いますが、Windows APIでは、メッセージループとも言います。

window.c


#include <windows.h>

/* メイン関数 */
int WINAPI WinMain(HINSTANCE hInstance,
		   HINSTANCE hPrevInstance,
		   LPSTR     lpCmdLine,
		   int       nCmdShow
		   )
{
  /* 変数の宣言 */
  HWND     hwnd;
  WNDCLASS wc;
  MSG      msg;
  
  /* ウィンドウクラスの初期化 */
  wc.style         = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc   = DefWindowProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hInstance;
  wc.hIcon         = NULL;
  wc.hCursor       = NULL;
  wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND + 1;
  wc.lpszMenuName  = NULL;
  wc.lpszClassName = TEXT("WINDOW");
  
  /* ウィンドウクラスの登録 */
  RegisterClass(&wc);
  
  /* ウィンドウの作成 */
  hwnd             = CreateWindow(TEXT("WINDOW"),
				  TEXT("Windows API Primer"),
				  WS_OVERLAPPEDWINDOW | WS_VISIBLE,
				  CW_USEDEFAULT,
				  CW_USEDEFAULT,
				  300,
				  200,
				  NULL,
				  NULL,
				  hInstance,
				  NULL
				 );
  
  /* メッセージループ */
  while (GetMessage(&msg, hwnd, 0, 0) >= 0)
    {
      DefWindowProc(hwnd, msg.message, msg.wParam, msg.lParam);
    }
  return 0;
}
    


実行結果


コード説明

  1. 各変数に使われている型の意味は次のようになります。
    HWND
    ウィンドウのハンドルが入る型です。プログラミングおいてハンドル(hundle)とは、データやコードの場所が変わる場合に、必ずその場所を追跡するポインターのポインターですが、Windows API ではデータやコードを識別するためのユニーク(一意)な整数値です。Hやhで始まる型やメンバーはハンドルです。
    WNDCLASS
    ウィンドウクラスの各設定値を格納する構造体です。
    MSG
    アプリケーションやシステムは色々なイベントを発します。そのイベントは、メッセージとして、この MSG 構造体に格納されます。イベントをきっかけに、アプリケーションの処理や振る舞いが決まる仕組みのことをイベント駆動(イベントドリブン、event driven)と言います。

  2. ウィンドウクラス構造体のメンバーの意味は次のようになります。
    style
    ここには通常、ウィンドウが水平方向に変更された時に再描画することを意味するCS_HREDRAW定数と、ウィンドウが垂直方向に変更された場合に再描画することを意味するCS_VREDRAW定数を | でつなげて設定します。
    lpfnWndProc
    Windows APIでは、ウィンドウがイベント(メッセージ)を発した場合に、それを処理する関数を決めておかなければなりません。ここでは Windows API ではじめから用意されている DefWindowProc を設定していますが、通常は自作した関数を設定します。Windows API で使われるメンバー名などの識別子には一定の接頭辞(プリフィックス、prefix)がつきます。l はロング、p はポインター、fn は関数(ファンクション、function)を表しています。
    cbClsExtra と cbWndExtra
    ここには、ウィンドウに追加で格納するデータ領域を設定しますが、通常は使わないので 0 を設定します。
    hInstance
    このウィンドウを使うアプリケーションを指定します。hInstance は、メイン関数の引数で指定された、このアプリケーションを表すハンドルです。
    hIcon
    アイコンを指定します。 NULL を指定した場合は標準のアイコンが設定されます。
    hCursor
    カーソルの形を指定します。NULL を指定した場合は、直前まで使われていたカーソルがそのまま使われます。
    hbrBackground
    ウィンドウの背景色を設定します。HBRUSHは、塗り潰しの情報が入ったハンドルです。システムカラーは、COLOR_BACKGROUND などの定数で指定しますが、必ず 1 を足さなければならない決まりになっています。
    lpszMenuName
    ウィンドウで使われるメニューを識別する文字列が入ります。メニューを使わない場合は NULL を指定します。sz は、終了文字 \0 で終わる文字列を意味しています。
    lpszClassName
    このウィンドウクラスの名前を設定します。後ほど、このウィンドウクラスからウィンドウを作成する場合に、この名前を使います。

  3. RegisterClass関数でウィンドウクラスをシステム (Windows OS) に登録します。引数には登録したいウィンドウクラスのポインターを指定します。

  4. CreateWindow関数でウィンドウを作成します。引数は次のとおりです。
    1. 第1引数に、どのウィンドウクラスからウィンドウを作るかを、そのウィンドウクラスの名前で指定します。
    2. 第2引数に、そのウィンドウのタイトルバーに表示する文字列を指定します。
    3. 第3引数に、作成するウィンドウの種類や状態を定数で指定します。WS_OVERLAPPEDWINDOWは他のウィンドウと重なり合うことを許可します。WS_VISIBLEは、ウィンドウを可視状態にします。通常作成されたウィンドウのデフォルトは不可視になっています。
    4. 第4引数に、ウィンドウが表示される位置を、デスクトップのX座標で指定します。CW_USEDEFAULTを指定するとシステムが適当な位置を選んでくれます。
    5. 第5引数に、ウィンドウが表示される位置を、デスクトップのY座標で指定します。CW_USEDEFAULTを指定するとシステムが適当な位置を選んでくれます。
    6. 第6引数に、ウィンドウの横幅を指定します。ここでもCW_USEDEFAULTを使うことができます。
    7. 第7引数に、ウィンドウの縦幅を指定します。ここでもCW_USEDEFAULTを使うことができます。
    8. 第8引数に、親ウィンドウを指定します。メインウィンドウを作成したい場合は、NULL を指定します。
    9. 第9引数に、メニューを指定します。メニューを使わない場合は、NULL を指定します。
    10. 第10引数に、そのウィンドウを使用するアプリケーションのハンドルを指定します。
    11. 第11引数は、ウィンドウ作成時に発生するイベントにデータを渡す手段として使用するそうですが、実際の使い方がわかりません。通常は、NULL で良いみたいです。

  5. GetMessage関数は、ウィンドウが発するイベント(メッセージ)を取得すします。引数は次のとおりです。
    1. 第1引数に、用意しておいた MSG 構造体のポインタを指定します。ここに取得したメッセージが格納されます。
    2. 第2引数に、メッセージを取得したいウィンドウを指定します。NULLを指定すると、そのアプリケーションのすべてのウィンドウからメッセージを取得します。
    3. 第3引数に、取得するメッセージの最小値を指定します。最小値を限定したくない場合は 0 を指定します。
    4. 第4引数に、取得するメッセージの最大値を指定します。最大値を限定したくない場合は 0 を指定します。
    GetMessage関数は、通常 1 以上の値を返します。エラーの場合は -1 を返します。そして終了メッセージ(WM_QUIT)を取得した場合にだけ 0 を返します。つまりFALSEが返され無限ループを抜けてアプリケーションが終了することになります。しかしここでは、-1 が返された場合だけ FALSE になるように条件式を変更しています。なぜなら今回使用したDefWindowProcはWM_QUITメッセージに対応していないからです。

  6. DefWindowProc関数でメッセージを処理します。引数は次のとおりです。
    1. 第1引数に、メッセージを送ってくるウィンドウのハンドルを指定します。
    2. 第2引数に、MSG構造体のmessageメンバーを指定します。messageメンバーはメッセージの種類を表す正数値です。
    3. 第3引数に、MSG構造体のwParamメンバーを指定します。wParamメンバーは、メッセージに付随する追加のデータが格納されている16bit整数値です。
    4. 第4引数に、MSG構造体のlParamメンバーを指定します。lParamメンバーは、メッセージに付随する追加のデータが格納されている32bit整数値です。
    このDefWindowProc関数は終了メッセージに対応していませんが、この関数を記述しておかないとウィンドウをクローズすることもできません。

    ウィンドウがクローズすると、ウィンドウのハンドルも破棄されます。するとそのウィンドウからメッセージを取得しようとするGetMessage関数でエラーの -1 が返されます。そして while文の >= 0 という条件を満たさなくなり無限ループを抜けることになります。少しトリッキーなコーディングですが、全体を簡単に把握しやすくするために、今回はこのようなコードにしました。


2049 visits
Posted: May. 31, 2020
Update: Jun. 03, 2020

ホーム   目次