Win32API>CreateWindowで窓を開く

このコーナーでは、クラスライブラリを使わずにWindowsのAPIを直接呼び出してWindowsプログラムを作ってみます。プログラムを起動した後、ウインドウを表示してウインドウに送られてくる「イベント」を受け取り処理を行う。そうしたWindowsアプリケーションの「内部の仕組み」をAPIで少しずつ実装してみましょう。

CreateWindowでウインドウを作る

今回は、CreateWindowというAPIでまず「窓を開く」(ウインドウを表示する)プログラムを書いてみました。プログラミング言語はCを使います。

プログラム

#include <windows.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow){

	HWND hwnd;
	MSG  msg;
	WNDCLASSEX  wndclass ;

	wndclass.cbSize        = sizeof(wndclass);        /* 構造体の大きさ */
	wndclass.style         = CS_HREDRAW | CS_VREDRAW; /* スタイル */
	wndclass.lpfnWndProc   = WndProc;                /* メッセージ処理関数 */
	wndclass.cbClsExtra    = 0;
	wndclass.cbWndExtra    = 0;
	wndclass.hInstance     = hInstance;             /* プログラムのハンドル */
	wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION); /* アイコン */
	wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);     /* カーソル */
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); /* ブラシ */
	wndclass.lpszMenuName  = NULL;              /* メニュー */
	wndclass.lpszClassName = "Test Window";   /* クラス名 */
	wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION);

	RegisterClassEx(&wndclass); /* ウインドウクラスTest Windowを登録 */

	hwnd = CreateWindow (
           "Test Window",        /* ウインドウクラス名 */
	   "窓を開く",           /* ウインドウのタイトル */
            WS_OVERLAPPEDWINDOW, /* ウインドウスタイル */
            32,32,               /* ウインドウ表示位置 */
            256,256,             /* ウインドウの大きさ */
            NULL,                /* 親ウインドウのハンドル */
            NULL,                /* メニューのハンドル */
            hInstance,           /* インスタンスのハンドル */
            NULL);               /* 作成時の引数保存用ポインタ */

	ShowWindow (hwnd,iCmdShow);      /* ウインドウを表示 */
	UpdateWindow (hwnd);

	while (GetMessage (&msg,NULL,0,0)) { /* メッセージループ */

		TranslateMessage(&msg);
		DispatchMessage(&msg);

	}

	return msg.wParam ;

}

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {

	switch (iMsg) {

		case WM_DESTROY : /* 終了処理 */

			PostQuitMessage(0);
			return 0;

	}

	return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

Windowsプログラムのエントリポイント(開始位置)は、WinMain()関数になります。Windowsプログラムが起動されると、まずこの関数が実行されるわけです。ウインドウを使うプログラムでは、この関数でメインのウインドウを作る事になります。

ウインドウクラス(WNDCLASSEX構造体)の属性

 ウインドウを作るためには、「ウインドウクラス」というものが必要です。これは、クラスといっても単なる構造体でこの構造体にウインドウの大きさだとか、どんなスタイルで表示するか、といったこれから作ろうとしているウインドウの設定を入れていきます。そして、このウインドウクラス(ウインドウの設定を入れた構造体)を作ったら、それを「登録」してCreateWindow()というAPIに渡せばウインドウクラスの設定に基づいたウインドウが作られるわけです。
なお、今回のプログラムでは以下のようなウインドウクラスを作りました。

今回のウインドウクラス

  wndclass.cbSize        = sizeof(wndclass);        /* 構造体の大きさ */
  wndclass.style         = CS_HREDRAW | CS_VREDRAW; /* スタイル */
  wndclass.lpfnWndProc   = WndProc;                 /* メッセージ処理関数 */
  wndclass.cbClsExtra    = 0;
  wndclass.cbWndExtra    = 0;
  wndclass.hInstance     = hInstance;               /* プログラムのハンドル */
  wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION); /* アイコン */
  wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);     /* カーソル */
  wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); /* ブラシ */
  wndclass.lpszMenuName  = NULL;              /* メニュー */
  wndclass.lpszClassName = "Test Window";   /* クラス名 */
  wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION);

 このように構造体としてウインドウの設定をするわけですが、今回の例は単に「窓を開く」だけなので、あまり気にせず適当に作ってあります。とはいえ、とりあえず内容を簡単にまとめてみましょう。

 まずstyleは、ウインドウのスタイルというより「処理」を決めるものです。例えば、終了ボタンをなくしてすぐには終了できなくするとか、サイズを変更した時にウインドウの内部を再描画するか、とかという処理方法を定義するようになっています。

CS_HREDRAW | CS_VREDRAWは、ウインドウのサイズが変更されたら再描画する、という指定です。次のlpfnWndProcは、メッセージを処理する関数へのポインタですが、これについては次回詳しく見ていく事にしましょう。

cbClsExtracbWndExtraは、設定用の追加領域ですが今回は使わないので0 を指定します。hInstanceは、ウインドウを使うプログラム(つまり自分自身)のハンドル(番号みたいなもの)です。hIconhCursor はそれぞれプログラムで使うアイコンとマウスカーソルで、今回はWindows に予め用意されているものを使いました。hbrBacground はウインドウの背景を塗りつぶす「ブラシ」を指定するもので、今回は「白」「無地」で塗りつぶすブラシになっています。lpszMenuはメニューの指定ですが、今回はメニューは使わないのでNULLです。

こうして作ったウインドウクラスは、RegisterClassEx()で登録します。そして、CreateWindow()で今登録したクラスに基づいてウインドウを作るわけです。

・CreateWindow
  hwnd = CreateWindow (
        "Test Window",        /* ウインドウクラス名 */
        "窓を開く",           /* ウインドウのタイトル */
         WS_OVERLAPPEDWINDOW, /* ウインドウスタイル */
         32,32,               /* ウインドウ表示位置 */
         256,256,             /* ウインドウの大きさ */
         NULL,                /* 親ウインドウのハンドル */
         NULL,                /* メニューのハンドル */
         hInstance,           /* インスタンスのハンドル */
         NULL);               /* 作成時の引数保存用ポインタ */

 クラス名は、先に登録したウインドウクラスの名前です。スタイルは、ウインドウをどう表示するかを決めるもので、今回はごく普通の「OVERLAPPEDWINDOW」にしました。

表示位置と大きさは、共に横・縦の順になっています。この数値を変えると、ウインドウが移動したり大きさが変わるので試してみてください。このプログラムにはウインドウは一つしかないので、親ウインドウはデスクトップになり、この場合親ウインドウのハンドルにはNULLを渡します。

メニューは、ウインドウクラス同様NULL ですね。インスタンスはプログラム自身のハンドルを、また作成時の引数を保存するためのポインタは今回は(というより普通は)使わないので、これもNULLです。
 これでウインドウが作成されました。CreateWindow()は戻り値にウインドウのハンドルを返すので、作られたウインドウに対する操作は以後このハンドルを使う事になります。

 では、いよいよウインドウを表示しましょう。表示するには、showWindow()というこれまたわかりやすい名前のAPI にウインドウのハンドルを渡してを呼び出すだけです。さらに、ウインドウの内容を再描画するためUpdateWindow()も呼び出しています。
 これでウインドウの表示部分については終わりです。プログラムの残りの部分は、「メッセージ」の処理なのですが、これについてはまた次回という事にしましょう。

プログラムソース表示

Visual Studio(Visual C++)でビルドする

最後に、プログラムのコンパイル方法を簡単にまとめておきます。

Visual Studio(Visual C++)を使用しますが、Windows SDKと他のコンパイラの組み合わせでも基本的には同じソースでビルドできると思います。

まず、プログラムのソースをダウンロードしたら、ファイル名の拡張子をCに変更してください(例api1.cなど)。

次に、Visual Studioを起動して「プロジェクト」を作ります。

まず「ファイル」メニューの「新規作成」で「Win32プロジェクト」を選びます。プロジェクトを保存するディレクトリを選んだら、「OK」ボタンをクリック。

Win32アプリケーションウイザードでは、アプリケーションの種類が「Windowsアプリケーション」になっていることを確認し、「空のプロジェクト」にチェックを入れてください。

Win32 プロジェクトが作成されたら、先ほどのソースファイルを追加する、あるいはCのソースファイルを「新規作成」し、コピペします。

これでビルドできる状態になりました。「デバッグ実行」してウインドウが表示されることを確認したら、ウインドウクラス(WNDCLASSEX)の属性やCreateWindowに渡す引数を変更して表示がどう変化するか試してみましょう。