Version 10.02
ウィンドウクラスを登録する
「今回、まずは前回の補足から。あ、前回のコードをそのまま載せると量が
あるから再掲載しません」
『前回のを見つつってことね』
「まず、関数を全部でみっつ作りました」
WinMain() : 一番最初に呼ばれる関数。下記2関数を呼びます。
CreateAndShowWnd() : ウィンドウを作って表示します。
RegistWndClass() : ウィンドウクラスを登録します。実装はこれから。
『なんで関数に分けたの?』
「ひとつの関数に多くの機能を詰め込むのは良くないからね。ごちゃごちゃ
して見にくいし、変数が多くなって管理できなくなるし」
『だからみっつに分けたわけね』
「一般には関数は1画面に収まるようにって言われてるから、それ超えそう
だったら分割を気にするようにね」
『1画面に収まるって、画面の解像度によって違うと思うけど……』
「その辺は適当にってことで。 true と false 、 bool 型について」
『 FALSE の方がなじみがあるけど』
「その辺については Version 5.10 ( No.075 ) を参照」
『 true と false の方が新しいのね』
「 API で bool を使ってるのはないから、どうしても BOOL の方がなじみ
深くなっちゃうけど、安全性なら bool の方が高いから」
『そうなの? TRUE と比較できないから?』
「 TRUE が 0 以外ってことは、どんな整数値でもいいってこと。それは、
BOOL が実は int 型だから」
『え!? int なの?』
「そう、 int を typedef したもの。 WinDef.h に書いてあるからあとで見
ておいて。で、 int ってことは、 TRUE と FALSE 以外の値も渡せるって事
だから」
『……それってかなり危なくない?』
「でも実際そういうプログラムが結構あるんだよね……」
『むむむ。あ、でも bool ならそれができないからいいってことなんだ』
「そういうこと。だから、自分のプログラムでは bool を使うクセをつけて
おいた方がいいかな」
『ほい』
「最後に、エラー処理について。今言った bool の戻り値は、ちゃんと
チェックするようにしてます。たとえば CreateAndShowWnd() の場合は」
bRes
= CreateAndShowWnd
( pchWndClassName
, p_hInstance
, p_iCmdShow
);
if( bRes == false )
{
return -1;
}
「って感じ」
『ひとつの関数ごとにこれするのって大変だよね』
「そうだね、例外処理を使うとまとめられるんだけど」
『れいがいしょり?』
「これはちょっと上級者向けでまだ使えないから、今の段階だとこういうふ
うにひとつひとつチェックすることになるかな」
『めんどうねー』
「でも、これは絶対にしなきゃ駄目だからね。よっぽど趣味のプログラム
で、自分でしか使わないようなプログラムでもない限り」
『つまり、他の人が使うようなのだったらこういうエラー処理は絶対必要っ
てわけね』
「そゆこと。確かに面倒だけど、これがないと使う側は何がなにやらだか
ら」
『でも、これでも何がなにやらだと思うけど』
「確かに……。本当はさらにエラーの原因まで調べてそれを出力させなきゃ
いけないんだけどね。それはまた別の機会に本格的に教えるから」
『それくらい重要って事ね』
「そゆこと。さて、では今回の本命、ウィンドウクラスの登録について説明
します」
『ウィンドウクラス、それを見ればウィンドウの素性が分かるってあれね』
「そう。ウィンドウクラスには大きく分けると次のふたつの種類がありま
す」
・組み込みウィンドウクラス:
ウィンドウズ内にすでに登録されているウィンドウクラス。
ダイアログ( #32770 )や各種ダイアログコントロール( BUTTON 等)。
・ユーザーが登録したウィンドウクラス:
プログラム内で普通に登録されたウィンドウクラス。
MFC によって登録されたものだと「 AFX なんたら」となる。
『組み込みのウィンドウクラスは CreateWindow() のリファレンスで見られ
るって言ってたよね』
「 Version 9.09 ( No.170 ) で触れたね。でもそれだけじゃなくて、ダイ
アログの #32770 とか、メニューの #32768 とかもそう」
『ええっ!? メニューのって!?』
「あ、メニューもウィンドウのひとつだから、ちゃんとウィンドウクラスが
登録されてて」
『それが使い回しされてるわけねー。でも、メニューのってどうしらべる
の? Spy++ じゃ無理なんじゃ……』
「オーナードローって憶えてる?」
『ボタンとかをプログラムで描くのだよね』
「メニューにもオーナードローが使えて、そのときにデバイスコンテキスト
が取得できて、デバイスコンテキストからは WindowFromDC() でウィンドウ
ハンドルが取得できて、そこから GetClassName() でクラス名を取得できる
……って、聞いてる?」
『聞いてるけどー、なんかマニアックー』
「大丈夫、火美ちゃんもできるようになったらしたくなるから」
『それはヤダ……』
「話を戻して、ウィンドウクラスには登録済みのものとプログラムから登録
するものがあって、ここで説明するのは後者の登録する方」
『登録も API とか使うの?』
「そう、 RegisterClassEx() って API を使います」
// ウィンドウクラスを登録します。
bool RegistWndClass
( const char *const p_pchWndClassName
, HINSTANCE p_hInstance
)
{
// ウィンドウクラス登録用構造体。
WNDCLASSEX stWndClassEx;
stWndClassEx.cbSize = sizeof( stWndClassEx );
stWndClassEx.style
= CS_BYTEALIGNWINDOW | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
stWndClassEx.lpfnWndProc = WndProc;
stWndClassEx.cbClsExtra = 0;
stWndClassEx.cbWndExtra = 0;
stWndClassEx.hInstance = p_hInstance;
stWndClassEx.hIcon = LoadIcon( NULL, IDI_EXCLAMATION );
stWndClassEx.hCursor = LoadCursor( NULL, IDC_ARROW );
stWndClassEx.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
stWndClassEx.lpszMenuName = NULL;
stWndClassEx.lpszClassName = p_pchWndClassName;
stWndClassEx.hIconSm = LoadIcon( NULL, IDI_EXCLAMATION );
// ウィンドウクラスを登録します。
if( RegisterClassEx( &stWndClassEx ) == 0 )
{
// 失敗しました。
return false;
}
return true;
}
『前回作った関数のひとつだね』
「この中でウィンドウクラスの登録をします。登録には WNDCLASSEX って構
造体を使います」
『この中に色々データを入れて、最後に登録するわけね』
「ではひとつひとつ見ていきます」
stWndClassEx.cbSize = sizeof( stWndClassEx );
『ん? sizeof って、変数のサイズを取ってくるのだよね』
「そう、まずこの構造体のサイズを入れます。実は API で使う構造体のほ
とんどは、こうやって自分自身のサイズを渡すから、まぁ習慣ってことで」
『 RECT とか入れてなかったじゃん』
「ああいうのは特別……じゃ、次」
stWndClassEx.style
= CS_BYTEALIGNWINDOW | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
「これはウィンドウクラスのスタイルです」
『ウィンドウスタイルみたいなもの?』
「そゆこと。でも、これを変えたからって大きく変わるものはないから、普
通はここにあるので十分。多すぎて困るってこともないみたいだし。じゃ、
次」
stWndClassEx.lpfnWndProc = WndProc;
『む、 Proc と言えばダイアログプロシージャ!』
「そのとおり。ダイアログの時はダイアログプロシージャ、ウィンドウの時
はウィンドウプロシージャを用意して、そこにメッセージを送ってもらいま
す」
『あれ? でも WndProc() って関数ないよね』
「それは次回作るから」
『あー。だからビルド通らないんだ』
「そゆこと。というわけで次へ」
stWndClassEx.cbClsExtra = 0;
stWndClassEx.cbWndExtra = 0;
「このふたつは特別な目的のためにメモリ領域を余分に使う時のもの。普通
は使わないから 0 にしておいて」
『普通じゃない時って?』
「独自のダイアログクラスを登録するときみたいだね。ちょっとよく分から
ないかも……じゃあ次」
stWndClassEx.hInstance = p_hInstance;
『インスタンスハンドルね』
「じゃあ次」
stWndClassEx.hIcon = LoadIcon( NULL, IDI_EXCLAMATION );
「 LoadIcon() については Version 3.6 ( No.031 ) を参照」
『でも第1引数が NULL って?』
「これはウィンドウズに最初から入ってるアイコンを使う場合。今回はとり
あえずのテストだから、アプリでアイコンを作らないからね。次の」
stWndClassEx.hCursor = LoadCursor( NULL, IDC_ARROW );
「も同じ。こっちはマウスカーソル。じゃ、次」
stWndClassEx.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
「これはウィンドウの背景色。 COLOR_BACKGROUND は標準の背景色」
『 HBRUSH なんだね。この背景色って Version 7.12 ( No.132 ) の背景色?』
「そう、あれのこと。だから逆に言うと」
『これはいつでも変えられるのね』
「そゆこと。次の」
stWndClassEx.lpszMenuName = NULL;
「はメニュー、なんだけどこれはあとでテストするから今は何もしません」
『メニューの練習もするんだ』
「 MFC 使ったときにしたからね。で、重要な次」
stWndClassEx.lpszClassName = p_pchWndClassName;
「これはウィンドウクラス名」
『ここで登録したウィンドウクラス名を、ウィンドウ作るときに指定するっ
てこと?』
「そゆこと。この部分は実はとっても重要だから。で、最後に」
stWndClassEx.hIconSm = LoadIcon( NULL, IDI_EXCLAMATION );
「これは小さい方のアイコン。 hIcon は大きいアイコン、この hIconSm は
小さい方のアイコンってこと」
『これで全部?』
「全部。パラメーター多いけど、実はそれほど重要なの多くないから。アイ
コンとかもあとから変えられるしね」
『 SetIcon() ?』
「そゆこと」
/*
Preview Next Story!
*/
『っと、なんかスパッと切れちゃったね』
「ちょっとオーバーしちゃったからね」
『ん? サイズが決まってるの?』
「いつも200行前後になるようにしてるから」
『へー、そうだったんだ』
「でも結構難しくてね」
『そりゃ、ネタによって分量って変わるよね』
「というわけで次回」
< Version 10.03 ウィンドウプロシージャ! >
『につづく!』
「この回は逆に足りなくって……」
『あらま』