Version 10.08
ウィンドウクラスとアトム
「今回は重要じゃないけど気を付けなきゃいけないことを紹介します」
『またそーゆー面倒そうなのを……』
「まぁね、しかも MFC 使ってるときには関係ないことだし」
『げ』
「で、今回はウィンドウクラスの話」
『そういえば Version 10.02 ( No.10.02 ) でなんか言ってたね』
「というわけで、まずはちょっとだけ修正してもらいます」
// ウィンドウクラスを登録します。
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 );
// ウィンドウクラスを登録します。
ATOM wAtom = RegisterClassEx( &stWndClassEx );
if( wAtom == 0 )
{
// 失敗しました。
return false;
}
return true;
}
『ウィンドウクラスを登録する関数だよね』
「そう。この RegisterClassEx() を呼んでる部分をちょっと変えました」
『戻り値を変数に入れてるね。でもこの ATOM ってなに? アトム? あの
アトム?』
「あのアトムがなんのアトムかはとりあえず置いとくとして、このアトムは
ウィンドウズシステム内で使う特殊なもののひとつ」
『特殊?』
「そう、特殊なもの。今回はこれが鍵。まずは…… Spy++ を見て」
『ほい』
「ウィンドウ一覧って」
0003008A "たすかぶ" TasKABWClass
「って行毎に書いてあるでしょ」
『 "" ってなってるのもあるけど』
「それはウィンドウ名がついてないウィンドウ」
『あ、これってウィンドウ名なんだ。ってことはその右側のは……』
「ウィンドウクラス」
『つまりさっきの RegisterClassEx() で登録したのね』
「ウィンドウクラス自体って実はそれほど重要じゃないんだけど、こういう
ふうに〈もうひとつのウィンドウ名〉になってるから、そういう意味では登
録するときのウィンドウクラス名は重要だから」
『ってことは、もしかして他とかぶっちゃいけない!? めんどくさー』
「あ、かぶっても大丈夫。たとえば……」
// 最初に呼ばれる関数です。
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// 下の行だけ修正。
const char pchWndClassName[] = "BUTTON";
bool bRes
= RegistWndClass( pchWndClassName, p_hInstance );
if( bRes == false )
{
return -1;
}
bRes
= CreateAndShowWnd
( pchWndClassName
, p_hInstance
, p_iCmdShow
);
if( bRes == false )
{
return -1;
}
return MessageLoop();
}
「って感じに pchWndClassName 、つまりウィンドウクラス名を BUTTON に
してみて」
『って、 BUTTON ってボタンのウィンドウクラスじゃない!』
「 Version 5.25 ( No.090 ) でボタンを実際に作ってると、無理そうで
しょ」
『だってそうでしょ? ボタンとしてもうウィンドウクラスが登録されてる
……けど大丈夫なの?』
「そう。試してみて」
『ほい、ビルドして実行。あ、全然問題ないや』
「これは、ウィンドウクラス名には〈グローバル〉なのと〈ローカル〉なの
があるから」
『グローバルとローカル?』
「普通に登録したウィンドウクラスは、実は〈ローカル〉なウィンドウクラ
スで、そのアプリ専用のものなんです」
『ってことは、他のウィンドウクラスとかぶってもいいんだ』
「逆に被っちゃいけないのは〈グローバル〉なウィンドウクラス。 BUTTON
みたいにウィンドウズシステム全体で使うようなウィンドウクラスはグロー
バルなウィンドウクラスとして登録されてるから」
『でもアプリだけで使うときには問題ない、と』
「まぁね。でもかぶっちゃうのはかぶっちゃうから。 Spy++ でこのアプリ
のウィンドウクラスを見てみて」
『普通に BUTTON になっちゃってるね。なんか混乱の元になりそう』
「でしょう。これはこれでね。だから、MFC のアプリは……たとえば VC++
のウィンドウクラスは」
『げ、 AFX:なんたらかんたらってわけ分かんないのになってる』
「こういうふうにして他と被らないようにしてるんです」
『でもこれはヤダな……』
「まぁでもある程度長くてアプリ名に近いものならたぶん被らないだろうだ
ろうけどね。かぶるのが怖い場合には、グローバルのウィンドウクラスを登
録してみるって方法もあるよ」
『もしグローバルで BUTTON を登録しようとすると?』
「エラーになるから。試してみようか」
// ウィンドウクラスを登録します。
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
| CS_GLOBALCLASS; // 追加。
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 = "BUTTON"; // 変更。
stWndClassEx.hIconSm = LoadIcon( NULL, IDI_EXCLAMATION );
// ウィンドウクラスを登録します。
ATOM wAtom = RegisterClassEx( &stWndClassEx );
if( wAtom == 0 )
{
// 失敗しました。
OutputDebugString( "ウィンドウクラス登録に失敗!\n" );
return false;
}
return true;
}
「まず WNDCLASSEX::style に CS_GLOBALCLASS を追加します。すると」
『ウィンドウクラスがグローバルに登録される、と。で、 BUTTON ってウィ
ンドウクラス名で登録すると……あ、ビルドして実行。うん、〈ウィンドウ
クラス登録に失敗!〉って出た』
「って感じになります。グローバルにしたいなら CS_GLOBALCLASS を追加す
ればいいってこと」
『ウィンドウを作る方は? CreateWindow() は何もしなくていいんだ』
「ウィンドウクラスはローカルなのから検索して、見つからなかったらグ
ローバルを探す、っていう仕組みになってるから」
『あー。……でもさ、実際、かぶっちゃうときってどうすればいいの?』
「そういうときは、プログラム側でかぶってるかどうか調べればいいんで
す。ここで ATOM が出てきます」
『やっと出てきたね、 RegisterClassEx() の戻り値』
「これはウィンドウシステム内で使われる整数値で、〈たったひとつしか存
在しちゃいけないもの〉に与えられる数字です」
『?』
「たとえば、ボタンのウィンドウクラス BUTTON が登録された時、
RegisterClassEx() の戻り値として、 BUTTON に割り当てられた ATOM 整数
値が返ってきます。これは GlobalFindAtom() で取得できます」
ATOM wButtonAtom = GlobalFindAtom( "BUTTON" );
「こう登録すれば、ウィンドウズシステム内で同じ BUTTON って名前で登録
できなくなります。そして、 BUTTON って文字列から必ず同じ ATOM 整数値
を取得できます」
『あー、よーするにウィンドウクラスの ID ナンバーなんだ』
「そういうこと。だから、この関数を呼んでもし登録済みだったら」
『……だったら?』
「ウィンドウクラス名をちょっとだけ変えて、また同じ関数で検索して、見
つかったらちょっと変えて、見つからなかったら登録」
『め、めんどくさ……』
「だからまぁ、普通はローカルなウィンドウクラスで十分だと思うよ」
『はーい』
「あ、そうそう、今回修正は全部始めの状態に戻しておいてください」
『ぅ”そうしないとアプリが動かないし……って、 ATOM の話ってそれだ
け?』
「実はこれだけ。アトムって、まぁ、本当はウィンドウクラス以外にも使わ
れてるんだけど、それでも使う事ってかなり少ないから……」
/*
Preview Next Story!
*/
『そんな、ほとんど使わないものも知らなきゃいけないの?』
「うーん、そんなこと言ったら MFC だけ使ってればいいことになるし」
『それじゃダメだの?』
「やっぱり、 MFC を使わなくてもアプリを作れないと」
『なんか、ものすごく大変なことをしようとしてる気がしてきた……』
「というわけで次回」
< Version 10.09 メニューふたたび >
『につづく!』
「メニューも MFC 使えば簡単だしね」
『そうよそうよー!!』