Version 5.27
メッセージを送ろう!
『この前は MFC と API 使って、コントロールのハンドルをゲットして操作
したんだよね』
「そして、今回は MFC を使わずにやってみましょう! じゃ、前回やった
ボタンの文字列を書き換える例から」
// いつもの。
void CFileTestDlg::OnBtnShow()
{
HWND hBtnWnd
= ::GetDlgItem( GetSafeHwnd(), IDC_BTN_SHOW );
::SetWindowText( hBtnWnd, "あいうえお" );
return;
// 後は取っておきましょう。
『……すっげー簡単じゃん』
「これは肩慣らしだからね」
『げ、そうなんだ。えーっとまず、前回も使った GetDlgItem() で
IDC_BTN_SHOW ボタンのウィンドウハンドルをゲットして、それを HWND 型
変数に格納っ』
「次の行もこの前の API 版」
『 CWnd::SetWindowText() じゃなくて、今回は API の SetWindowText()
ね。ビルドして実行、ボタンが〈あいうえお〉になった!』
「とういうわけで、 API の操作はこれが基本。じゃ、次に、前回の最初に
やったリストボックスの操作をしましょう」
『はーい!』
// いつもの。
void CFileTestDlg::OnBtnShow()
{
HWND hLstWnd
= ::GetDlgItem( GetSafeHwnd(), IDC_LST_DATA );
::SendMessage( hLstWnd, LB_ADDSTRING, 0, (LPARAM)"あいうえお" );
return;
// 後は取っておきましょう。
『なんだ、初見の API ひとつ増えただけじゃん』
「最初の行はさっきと同じ」
『 IDC_LST_DATA リストボックスのウィンドウハンドルをゲットして、それ
を次の SendMessage() っていう API で使ってるんだよね』
「とりあえず動作確認」
『ビルドして実行! うん、ちゃんとリストボックスに〈あいうえお〉って
出たよ。つまりこの SendMessage() っていうのが追加してくれてるんで
しょ?』
「もちろんそれはそう。でもどうやって追加してると思う?」
『んー、えっと、第1引数が追加先のウィンドウハンドルでしょ、第2引数、
CListBox::AddString() に似てるから、なんかこれが〈追加っ!〉ってこと
なのかな。あと第4引数が追加する文字列。 LPARAM っていうのにキャスト
してるけど』
「じゃ、 MSDN の LB_ADDSTRING のページを見てみて」
『ほい。……日本語のページ出ないよ?』
「そう、日本語のページはないんです」
『なんで?』
「メッセージには日本語のリファレンスがないんです」
『メッセージ?』
「そう。〈キーワード〉欄に LB_ っていうのがいっぱいあるでしょ。これ
全部メッセージ」
『うわいっぱい!』
「メッセージは、ウィンドウを操作するために、ウィンドウに送るもの」
『あ、だから LB_ADDSTRING を SendMessage() で送ると、リストボックス
に追加できたんだ!』
「そういうこと。本来は、ウィンドウやコントロールを操作するためにはこ
のメッセージを送る必要があります」
『本来? ……そっか、 MFC もこのメッセージっていうの送ってるんだ!』
「そういうこと」
『ってことは…… CListBox のメンバ関数、このメッセージって言うのと似
たのばっかり』
「 CListBox::GetCount() は LB_GETCOUNT 、CListBox::GetTopIndex() は
LB_GETTOPINDEX 」
『だから』
「ちょっと待って! えっと、この前の……」
// いつものメンバ関数。
void CFileTestDlg::OnBtnShow()
{
CListBox *pcListBox
= (CListBox *)GetDlgItem( IDC_LST_DATA );
pcListBox->AddString( "あいうえお" );
return;
// あとは取っておきましょう。
「前回最初のプログラムだね」
『 AddString() してるとこにブレークポイント置いて、ビルドして実行、
ステップイン……ホントだ、 CListBox::AddString() の中で
::SendMessage() で LB_ADDSTRING 送ってる!』
「それだけじゃなくて」
『ほ、他の CListBox のメンバ関数も LB_ なメッセージ送ってる!』
「これで、コントロールをどうやって操作していたか分かった?」
『うん、分かった。でも結構簡単だよね』
「操作するっていう部分だけならね」
『どゆこと?』
「それはあとのお楽しみ。もう少し SendMessage() について見ておこうか」
『はーい』
::SendMessage( hLstWnd, LB_ADDSTRING, 0, (LPARAM)"あいうえお" );
「 SendMessage() の宣言部も書いておこうか」
LRESULT SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
『第1引数はリストボックスのウィンドウハンドルだね』
「この第1引数のウィンドウにメッセージを送ります」
『第2引数はメッセージだよね。あ、 UINT ってことは unsigned int なん
だ』
「 API は整数値でほとんど間に合わせてるから。ウィンドウスタイルや、
コントロールの ID もそうだったでしょ」
『メッセージもどっかで #define とか const int とかされてるの?』
「されてるよ、 winuser.h とかで。 #define だね」
『第3引数は使わないみたいだけど、じゃーなんで第3引数あるの?』
「それは、第3引数と第4引数はメッセージによって使い道が違うから」
『違うってどういうこと?』
「 WPARAM は unsigned int 、 LPARAM は long 、つまりどっちも」
『32ビット整数値の符号なしとありね』
「で、この32ビットサイズに収まるものならなんでも渡しちゃおう、って
いうのがメッセージの考え方」
『……つまりそれは、整数値も送るし、文字列ポインタも送る、ってことな
のね。なんかそれって危なくない?』
「危ないよ。メッセージを取り間違えて、キャストし間違えたらアウトだか
らね」
『なんでそんな危険なことすんの?』
「そりゃ、メッセージっていうシステムひとつで、いろんなことをできるよ
うになってるからね」
『うん、確かに、 LB_ なメッセージ、いろんなの送れて、いろんなことで
きるっていうのは分かるんだけど、わざわざそんなことしなくていーじゃん。
関数にしちゃえばいいのに』
「あ、そっか、これは受け取る側のことを知らないと……」
『受け取る?』
「 LB_ADDSTRING を送ったらリストボックスに文字列が追加される、ってい
うのももちろんプログラムとして作られてるんだけど」
『うん』
「そのプログラムの中では、送られてきたメッセージをそのまま受け取って
るんです」
『そのまま受け取る?』
「 MSDN で WindowProc() っていう API のリファレンスを開いて」
『ほい』
LRESULT CALLBACK WindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
『あ、 SendMessage() と同じだ!』
「これを〈ウィンドウプロシージャ〉って言います」
『うぃんどうぷろしーじゃ?』
「ウィンドウはこのウィンドウプロシージャを持ってて、メッセージが送ら
れてくるとこの関数が呼ばれて、引数がそのまま渡されるんです」
『ウィンドウはって、じゃあこのダイアログとかも?』
「もちろん、今プログラムしてる FileTest もね。簡単に見ておこうか。
CFileTestDlg::OnBtnShow() の中でブレークポイントをセットして」
『さっき AddString() にセットしてたからそれでいーや』
「実行して止まったら、メニューの【表示】−【デバッグウィンドウ】
−【コールスタック】を選んで」
『あ、初めてのだ』
「コールスタックは、今実行してる関数を呼んだ関数、さらにその関数を呼
んだ関数、っていうふうに関数をさかのぼって見ることができるもの」
CFileTestDlg::OnBtnShow() line 186
_AfxDispatchCmdMsg() line 88
(略。あと小カッコの中身も略)
AfxWndProc() line 368
AfxWndProcBase() line 220 + 21 bytes
KERNEL32! bff7363b()
KERNEL32! bff94497()
『うわー、 CFileTestDlg::OnBtnShow() までにこんなにいっぱい関数が呼
ばれてるんだ』
「下から3行目の AfxWndProcBase() をダブルクリックして」
『その関数が見られるんだね』
AfxWndProcBase(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
『あああっ、その WindowProc() っていうのと引数が同じだ!』
「メッセージが送られてくるとこの関数が呼ばれて、各引数がそのまま渡さ
れます」
『つまり、メッセージが送られてきたのをこの関数で受け取るのね』
「こういう、たったひとつの関数で受け取る仕組みになってるから、いろん
な引数は使えないわけ」
『その代わり、 WPARAM と LPARAM にいろんな型を詰め込むのね』
「そういうこと。でもこれじゃ不便だから、メッセージに合わせて引数を変
更して、最終的に CFileTestDlg::OnBtnShow() が呼ばれる、みたいな仕組
みを MFC が持ってるってわけ」
『なるほど。……ちょっと待った!! って事はつまり、ボタン押したって
ことそのものもメッセージだってゆーの!?』
「というわけで次回に続く!」
/*
Preview Next Story!
*/
『楽しいけど、怖いよね』
「なにいきなり」
『プログラムの仕組みとか分かってくると、なんか怖いかも』
「確かに、なんかタブーに触れてるって感じはあるかな」
『というわけで次回』
< Version 5.28 イベント=メッセージ >
「につづく!」
『たとえるなら、初めて〈つゆだく〉を頼んだ時の感覚?』
「っておまえ食ったんかい!」
『はぁ〜い』