パソコン活用研究C&C++であそぼ(C、C++、の活用研究)

正しくないWindowsプログラム(とりあえずウィンドウを表示する)その2
--ウィンドウ構造体を定義--


今回も、メッセージループとウィンドウプロシジャがないプログラムですが、一歩進んで自分でウィンドウクラス構造体を定義して、ウィンドウを表示させてみたいと思います。ウィンドウプロシジャがないので、ウィンドウプロシジャの関数(関数名=関数のポインタ)を指定すべきところ(wcex.lpfnWndProc)には、DefWindowProc関数を指定しておきます。DefWindowProc関数については、ウィンドウプロシジャの説明の時に触れたいと思いますが、この関数の機能についても簡単に実験をしてみたいと思います。


window0.cpp
#include<windows.h>

int WINAPI WinMain(
HINSTANCE hInstance ,
HINSTANCE hPrevInstance ,
PSTR lpCmdLine ,
int nCmdShow ) {
HWND hwnd;
WNDCLASS wcex;

//ウィンドウクラス構造体の定義
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = DefWindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL , IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL , IDC_ARROW);
wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "AppModel";

if (!RegisterClass(&wcex)) return 0;

hwnd = CreateWindow(
wcex.lpszClassName , "test" ,
WS_OVERLAPPEDWINDOW ,
100 , 100 , 200 , 200 , NULL , NULL ,
hInstance , NULL
);

if (hwnd == NULL) return 0;

ShowWindow(hwnd , SW_SHOW);
MessageBox(NULL , "" ,"" , MB_ICONINFORMATION);

return 0;
}

ウィンドウクラス構造体の定義
wcex.style には CS_HREDRAW | CS_VREDRAWと指定したので、垂直方向、水平方向どちらにウィンドウのサイズが変更されても再描画されるウィンドウになります。
wcex.lpfnWndProc 本来ここにはウィンドウプロシジャを指定しますが、ここでは DefWindowProc関数を指定しました。DefWindowProc関数はWindowsから伝達されるメッセージのデフォルト処理をしてくれる関数です。ウィンドウのサイズの変更による再描画や、×ボタンでウィンドウを破棄するといういような処理をやってくれます。
wcex.hbrBackground ウィンドウの背景色になりますが、 (HBRUSH)GetStockObject(WHITE_BRUSH)は白色の背景色の指定になります。


CreateWindow
第3パラメータでWS_OVERLAPPEDWINDOWを指定していますが、普段みかける最小化、最大化ボタンをもった一般的なウィンドウのスタイルで表示します。

ShowWindow
ShowWindowの後でMessageBoxを呼んでいるのは、そのまま終了してしまうのを一時停止させるためのものです。普通は、こんなところでMessageBoxを呼んだりはしません。

実行してみる
さて、このプログラム(window0.exe)を実行してみます。
ようやくよく見かける最小化、最大化ボタンをもった一般的なウィンドウに近い?ものができました。ここまでできると頂上まであと一歩のような気もしてきますね。



ウィンドウクラス構造体の定義で
wcex.style = CS_HREDRAW | CS_VREDRAW; -->垂直方向、水平方向どちらにウィンドウのサイズが変更されても再描画されるスタイルに指定
wcex.lpfnWndProc = DefWindowProc; -->ウィンドウプロシージャにはDefWindowProcを指定してありますが、このDefWindowProcという関数は、ウィンドウのサイズの変更による再描画を自動的にやってくれます。
従って、このウィンドウのサイズを変更することも可能です。マウスでちょっとサイズを変更してみます。



DefWindowProc
また、DefWindowProcは最大化ボタンをクリックするとウィンドウを最大化する、×ボタンでウィンドウを破棄するといういような処理もやってくれます。試しに×ボタンをクリックしてみると、メインウィンドウは消えて、メッセージボックだけになります。メッセージボックスの[OK]ボタンをクリックするとこのプログラムは終了です。
DefWindowProcについて、ここでは詳しい説明はしませんが。

もう、ほとんど一般的なウィンドウと同じですね。頂上が見えてきた感じがしますが、まだかなり不完全です。少しがっかりするかもしれませんがWindowsの動く仕組みを理解しつつ進むという、なんとも難儀な話しになりますので、じっくり実験してみたいと思います。Windowsプログラムを作成するということは、WindowsというOSの仕組みそのものを理解する必要がありますので、もう少し横道におつきあいいただきながらいきましょうか。
今回はウィンドウプロシージャの指定で、直接DefWindowProc関数を指定していますが、この関数は、、本来は自分で記述したウィンドウプロシージャの中で使うものです。元気があれば、Windowsプログラムの正しい雛形(中篇)で確認してみて下さい。以下にウィンドウプロシージャの部分のみ掲載しておきます。
// ウインドウプロシージャ
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);

}
}

DefWindowProcは自分で記述したウィンドウプロシージャのなかで、デフォルト処理(自分が処理の記載をしなかったメッセージの処理)を任せる、という使い方が正しいWindowsプログラムになります。

さらに、今回の実験プログラムでは省いていますが、本来は以下のようなメッセージループというWindowsからのメッセージをうけとり続ける部分があり、Windowsからのメッセージはここでいったん受け取ることになります。メッセージループでいったん受けとったメッセージはDispatchMessage関数でウィンドウプロシージャに送られることになります。メッセージループについてはここでは説明は省きますが、今回のプログラムはこのメッセージループを記述していないので、画面を表示するとすぐに消えちゃいます。消えると困るので、メッセージボックスを呼んで一時停止させています。

// メッセージループ
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
というわけで、メッセージループとウィンドウプロシージャを理解すると、ようやく正しいWindowsプログラムの完成になります。とは言っても、そては、ただウィンドウを表示するだけのプログラムですが。


TopPage