パソコン活用研究シリコンバレー(C、C++、の活用研究)

Windowsプログラムの正しい雛形(中編)
--ウィンドウの作成と表示--
1 Windowsプログラムの雛形のおさらい

”正しい”Windowsプログラムは、エントリポイント(プログラムの開始)である(1)WinMainと、Windowsから呼び出されて渡されたメッセージを処理する(2)ウインドウプロシ−ジャ(通常WinProcと書く)から成り立ちます。

そして(1)WinMainの中は、
(A) ウインドウクラスの登録
(B) ウィンドウの作成
(C) ウインドウの表示
(D) メッセージループ
の4つのパートから成り立ちます。

ということでしたね。枠組みだけざっとおさらいしてみましょう。

//(1)Winmain
WinMain (
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow )
{
}
// ウィンドウクラス構造体を設定

//(A) ウインドウクラスの登録
RegisterClassEx

// (B)ウインドウを作成
CreateWindow

//(C) ウインドウを表示
ShowWindow
UpdateWindow

//(D) メッセージループ

//(2) ウィンドウプロソジャ
WndProc (
HWND hWnd,
UNIT message,
WPARAM wParam,
LPARAM lParam )
{
}

以下、Visual C++で「Win32 Application」を選択し、そのうち「空のプロジェクト」を選択した時に自動的に生成される、ただウインドウを表示するだけの空っぽのプログラムです。
これが、いちおうWindowsプログラムの”正しい”雛形と考えて下さい。
#include "windows.h"

// 関数のプロトタイプ宣言
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// エントリポイント
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow )
{
WNDCLASSEX wcex; // ウインドウクラス構造体
HWND hWnd; // ウインドウハンドル
MSG msg; // メッセージ構造体

// ウィンドウクラス構造体を設定します。
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "ModelApp";
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

// ウインドウクラスを登録します。
RegisterClassEx(&wcex);

// ウインドウを作成します。
hWnd = CreateWindow(wcex.lpszClassName, // ウインドウクラス名
"Cursor", // キャプション文字列
WS_OVERLAPPEDWINDOW, // ウインドウのスタイル
CW_USEDEFAULT, // 水平位置
CW_USEDEFAULT, // 垂直位置
CW_USEDEFAULT, // 幅
CW_USEDEFAULT, // 高さ
NULL, // 親ウインドウ
NULL, // ウインドウメニュー
hInstance, // インスタンスハンドル
NULL); // WM_CREATE情報

// ウインドウを表示します。
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

// メッセージループ
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

// 戻り値を返します。
return msg.wParam;
}

// ウインドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
// メッセージの種類に応じて処理を分岐します。
switch (message)
{

case WM_DESTROY:
// ウインドウが破棄されたときの処理
PostQuitMessage(0);
return 0;
default:
// デフォルトの処理
return DefWindowProc(hWnd, message, wParam, lParam);
}
}


2 ウィンドウの作成
ウィンドウクラスの登録がすんだら、実際にウィンドウの作成を行います。ここで初めてメモリー上にウィンドウの実体ができます。
ウィンドウの作成には、CreateWindowという関数を使います。CreateWindowは11個の引数を使います。
では、CreateWindow関数の11個の引数は以下の通りです。
HWND CreateWindow(
LPCTSTR lpClassName, // 登録されているウィンドウクラス名文字列へのポインタ
LPCTSTR lpWindowName, // ウィンドウタイトルの文字列へのポインタ
DWORD dwStyle, // ウィンドウスタイル
int x, // X 座標
int y, // Y 座標
int nWidth, // ウィンドウの幅
int nHeight, // ウィンドウの高さ
HWND hWndParent, // 親ウィンドウのウィンドウハンドル
HMENU hMenu, // メニューハンドル
HANDLE hInstance, // アプリケーションハンドル
LPVOID lpParam // WM_CREATE メッセージに送るパラメータ
);


”正しい”雛形(Visual C++で「Win32 Application」を選択し、そのうち「空のプロジェクト」を選択した時に自動的に生成される、ただウインドウを表示するだけの空っぽのプログラム)では、以下のようにコーディングされています。
hWnd = CreateWindow(
wcex.lpszClassName, // ウインドウクラス名
"Cursor", // キャプション文字列
WS_OVERLAPPEDWINDOW, // ウインドウのスタイル
CW_USEDEFAULT, // 水平位置
CW_USEDEFAULT, // 垂直位置
CW_USEDEFAULT, // 幅
CW_USEDEFAULT, // 高さ
NULL, // 親ウインドウ
NULL, // ウインドウメニュー
hInstance, // インスタンスハンドル
NULL); // WM_CREATE情報



戻り値
まず、この関数の戻り値ですが、戻り値として HWND型 の値を返します。これはウィンドウハンドルと呼ばれるものです。ウィンドウハンドルとはウィンドウオブジェクトへの参照です。と言っても最初のうちは何のことかわからないかもしれませんが、ウィンドウハンドルとは、個々のウィンドウを識別するための値です。
Windowsはマルチタスクですから、画面上にはいろんなアプリケーションのいろんなウィンドウが表示されていると思います。このとき、例えばキーボードの"A"をおした、あるいはマウスをクリックした、という操作をした時、たくさんのウィンドウのうち、どれに"A"が押されたことを通知したらいいのか、またマウスのクリックというイベントをどのウィンドウに渡したらいいのか、区別して適切なウィンドウに渡さなくてはなりません。このために、1個のウィンドウには識別用に1個の値が付けられます。これがウィンドウハンドルです。
CreatWindowを実行した時に、ウィンドウハンドルは戻り値としてかえってきますので、今後、このウィンドウを操作するには、いろいろな関数にこのウィンドウハンドルを渡すことでどのウィンドウを操作するのかを知らせることができる仕組みになっています。

CreateWindow() 関数はウィンドウの生成に失敗すると NULL を返します

第1引数
第一引数では、OS に登録してあるウィンドウクラス名の入った文字列を指定します。OSにはあらかじめ、テキストボックスやラベルやボタンといった基本的なウィンドウクラスが定義されています。それ以外のウィンドウクラスを指定する場合は、ウィンドウを作成する前に、作成したいウィンドウの雛形となる情報が、あらかじめ登録されていなければなりません。”正しい”雛形(Visual C++で「Win32 Application」を選択し、そのうち「空のプロジェクト」を選択した時に自動的に生成される、ただウインドウを表示するだけの空っぽのプログラム)でも、事前にウィンドウクラス構造体の設定と、RegisterClassでウィンドウクラスの登録をするようになっています。
VC++の”正しい”雛形”では、第一引数は、ウィンドウクラス構造体のメンバのウィンドウクラス名で決めた、wcex.lpszClassName になっています。

第2引数
第 2 引数には、キャプション(タイトル)文字列を指定します。
第3引数
第 3 引数には、ウィンドウスタイルを指定します。代表的なものを挙げると、ウィンドウスタイルには次のようなものがあります。
説明
WS_BORDER 境界線を持つウィンドウを作成します。
WS_HSCROLL 水平スクロールバーを持つウィンドウを作成します。
WS_MAXIMIZE ウィンドウを最大化の状態で作成します。
WS_MAXIMIZEBOX 最大化ボタンを持つウィンドウを作成します。WS_SYSMENU スタイルも指定する必要があります。WS_EX_CONTEXTHELP スタイルと組み合わせることはできません。
WS_MINIMIZE ウィンドウを最小化の状態で作成します。WS_ICONIC スタイルと同じです。
WS_MINIMIZEBOX 最小化ボタンを持つウィンドウを作成します。WS_SYSMENU スタイルも指定する必要があります。WS_EX_CONTEXTHELP スタイルと組み合わせることはできません。
WS_OVERLAPPEDWINDOW WS_OVERLAPPED スタイル、WS_CAPTION スタイル、WS_SYSMENU スタイル、WS_THICKFRAME スタイル、WS_MINIMIZEBOX スタイル、WS_MAXIMIZEBOX スタイルを持つオーバーラップウィンドウを作成します。
WS_POPUP ポップアップウィンドウを作成します。このスタイルは、WS_CHILD スタイルと一緒には使えません。
WS_SYSMENU タイトルバー上にウィンドウメニューボックスを持つウィンドウを作成します。WS_CAPTION スタイルも指定する必要があります。
WS_THICKFRAME サイズ変更境界を持つウィンドウを作成します。WS_SIZEBOX スタイルと同じです。
WS_VISIBLE 可視状態のウィンドウを作成します。
WS_VSCROLL 垂直スクロールバーを持つウィンドウを作成します。

”正しい”雛形は "WS_OVERLAPPEDWINDOW" スタイルを使っています。このスタイルはタイトルバー、システムメニュー、最大化ボタン、最小化ボタンをもち、サイズ変更可能なウィンドウです。いわゆる、ごく一般にあるメインウィンドウはこのスタイルです。

第4,5,6,7引数
第 4, 5, 6, 7 番目の引数は、ウィンドウの大きさや位置を指定します。

第8引数
第 8 番目の引数には、親ウィンドウのウィンドウハンドルを指定します。MDI (Multi Document Interface)アプリケーションの場合などに指定します。親ウィンドウがない場合は NULL を指定します。

第9引数
第 9 番目の引数は、メニューハンドルを渡します。メニューを使わない場合は、NULL を指定します。

第10引数
第 10 番目の引数には、インスタンスハンドルを渡します。WinMain の 1 番目の引数ですね。

第11引数
第 11 番目の引数には、WM_CREATE メッセージ(ウインドウが作成されたことを通知するメッセージです)へのパラメータを渡します。CreateWindow もしくは、CreateWindowEx 関数でウィンドウが作成された直後にWM_CREATE メッセージが OS から送られます。そのときに渡したいパラメータがあればここに指定します。使わないときは、NULL にします。

3 ウィンドウを表示
ウィンドウの表示には、ShowWindow関数と、UpdateWindow関数という2つのAPI関数を使用します。
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

ShowWindow関数の第1引数はウィンドウハンドルです。第2引数はWinMainの第4引数が渡されます。
WinMainの第4引数 nCmdShowは、プログラム起動時にウィンドウをどのような状態で表示するかのパラメータでした。通常状態、最小化、最大化などが指定されます。
ShowWindow関数によって、いわゆる非クライアント領域(境界線、キャプション、メニューバーなど)が表示されます。

UpdateWindow関数の引数もウィンドウハンドルです。この関数はクライアント領域(アプリケーションが自由に使える領域)の描画を行います。UpDateWindow関数が呼び出されると、OSから引数のウインドウハンドルで指定されたウィンドウにWM_PAINTメッセージが送られます。

WM_PAINTメッセージは何?という話や、そのメッセージが送られたからどうなるの、という話はメッセージループやウインドウプロシージャのところでおいおい説明していきますので、ここでは忘れてしまって結構です。

4 今後
さて、Windowsプログラムの正しい雛形ということで、中篇までやってきましたが、ウィンドウを表示するだけなのにまだやっと道半ばというところです。初めての方ははやれやれという感じでしょうし、正直どの関数がどういう働きをするのかピンとこないと思います。
中篇までの知識は、とりあえず本当にただウインドウを表示するだけで、ウインドウを閉じるとか、プログラムを終了するとかいった基本機能もまったく実装していません。しかし、ここまでの知識で、正しくないウインドウプログラムを作って、ちょっとこれまでの復習をしてみると、これまでの説明の意味が良くわかるでしょう。数回にわけて正しくないウインドウプログラムという説明をやってみようと思います。これまで、なんかもうひとつわかんなかったという人は是非、正しくないウインドウプログラムというやつを勉強して見て下さい。


TopPage