Version 5.29
ウィンドウとコントロールのまとめ
「というわけで、今回はコントロール編のまとめ」
『ってことは Ver 6.0 に!?』
「行きません。まだアプリ完成してないでしょ」
『ホントに1年経っちゃうよ……』
「ま、この際それは気にしないことにしましょう。まず Ver 5.23 ( No.088 )
でウィンドウクラスっていうのについて見てみました」
『ウィンドウを作る元になるのだよね。登録する必要があったし』
「これはそんなに重要じゃないかな。次は Ver 5.24 ( No.089 ) のウィン
ドウスタイル」
『ウィンドウのタイトルがあるかーとか、ボタンを平らにするーとか、そう
いうのを決められるんだよね』
「これは結構重要。たとえば、他のアプリ見て〈こんなスタイルにしたい
なー〉って思ったら」
『そのアプリを Spy++ で見てスタイル調べる!』
「そして自分のアプリで同じ設定にすればいいわけだ。ただ、そう簡単にい
かない場合もあったりするんだけどね」
『そなの?』
「たとえば、ウィンドウによっては自前でタイトルバー描いてたり」
『めんどそう……』
「そういうの真似する場合は、同じように描画しなきゃいけなかったりして
面倒なことになるかもね」
『次は Ver 5.25 ( No.090 ) 。 CreateWindow() でボタンを作りました!
これ、ホントに驚いたんだけど』
「ボタンなんかのコントロールは全部ウィンドウの一種だから、こういうふ
うに作ることができるわけ。実際に自分でやってみると実感するでしょ」
『するする。あと、毎度おなじみ MFC バージョンのも』
「 CButton のだね。この CButton の例で、 CButton の変数がグローバル
変数になってること、ちょっと憶えといて」
『重要?』
「ちょっとね。 Ver 5.26 ( No.091 ) はダイアログにあるコントロールを
取得する方法を紹介しました」
『コントロールは ID ナンバーが付いてるからそれで拾えるんだよね。でも
変なキャストしなきゃいけなかったり』
「キャストしない方法はウィンドウハンドルを取得してそれを操作するって
いうの」
『まず CButton に Attach() したんだよね。これが、 Detach() しないと
エラーになっちゃうってゆー』
「実は、これがさっきの CButton をグローバル変数にするっていうのと関
係があるんだよね。なんで Detach() したんだっけ」
『 CButton 変数がなくなるときにボタンを消そうとしちゃうから』
「もし Ver 5.25 ( No.090 ) の例で CButton の変数を普通の変数にしちゃ
ったら?」
『こっちも……そっか、ボタンが消えちゃうんだ! 作ったばかりなのに』
「そういうこと。だからわざわざこういう面倒なことをしてるわけ」
『ほー。 MFC も結構面倒なところあるんだね』
「これは設計方針がややこしいのかも。 Ver 5.27 ( No.092 ) ではメッ
セージを送ってリストボックスを操作してみました」
『あれ、でもさ、最初に SetWindowText() でウィンドウのタイトル変えら
れたよね。メッセージじゃないのもあるんだ』
「そういうわけじゃないよ。 SendMessage() のリファレンス見てみて」
『あ、 WM_SETTEXT を送るって書いてある! WM_SETTEXT ってメッセージ
だよね』
「そう、ウィンドウのタイトルを変えるメッセージ。このメッセージを使っ
てもウィンドウのタイトルを変えられます。たとえばこんなふうに」
// いつもの。
void CFileTestDlg::OnBtnShow()
{
HWND hLstWnd
= ::GetDlgItem( GetSafeHwnd(), IDC_LST_DATA );
::SendMessage( hBtnWnd, WM_SETTEXT, 0, (LPARAM)"あいうえお" );
return;
// 後は取っておきましょう。
『なんだ、結局メッセージ使ってるんだね』
「ウィンドウを操作する方法は、ほとんどがメッセージを送ることになるか
ら」
『ほとんどってことは、メッセージじゃできない操作もあるの?』
「あるよ。たとえばウィンドウスタイル。ウィンドウスタイルを取得したり
変更したりは、 GetWindowLong() と SetWindowLong() っていう API を使
わなきゃできません」
『これはメッセージじゃできないんだ』
「逆に、もしウィンドウやコントロールをこう操作したい、っていうのを探
す場合には」
『メッセージの一覧を見て探せばいいんだね』
「そういうこと。メッセージの見方だけど、頭に WM_ が付いてるのがすべ
てのウィンドウ、コントロール共通のメッセージ」
『 WM_SETTEXT がそうね。って、これ、共通じゃない方が分かりやすかった
んじゃない? ウィンドウもボタンも一緒って……』
「 API はそういうところあるからねぇ。メッセージは膨大だし、全部英文
だし、探すのは結構大変かも」
『 MFC の方からっていうのは?』
「それもいいかも。 MFC の CWnd メンバ関数は、ほとんどがメッセージを
送るものだから、メンバ関数一覧から探した方が楽かも」
『あとはコントロール別のね、 BM_ とか LB_ とか』
「他のコントロールのもあるから。これも MFC の各コントロールクラスの
メンバ関数一覧から調べた方が楽かも」
『あれ? BM_ じゃなくて BN_ っていうのがある。誤植?』
「これは〈通知メッセージ〉っていうの。 Notify Message の N が付いて
るんで BN_ 」
『のてぃふぁいめっせーじ? そういえば LBN_ とかあるね』
「普通のウィンドウのは NM_ 。通知メッセージは〈何かが起きた〉ってい
うのを報せるメッセージ」
『へ? だって WM_MOUSEMOVE とかもそうじゃない』
「そう。ここで重要なのは、通知メッセージを受け取るのは、イベントが発
生したウィンドウじゃなくて、その親ウィンドウってこと」
『親ウィンドウ……コントロールだとダイアログってこと?』
「そういうこと。もしメッセージが、イベントが発生したウィンドウだけに
しか送られないとしたら、ボタンを押したとき」
『そのボタンにしか、押されたってメッセージ送られない。だから、その
メッセージ処理できない!』
「だから、押されたときは親ウィンドウに〈ボタンが押された!〉っていう
のを通知するんです。それが通知メッセージ」
『ってことは CFileTestDlg::OnBtnShow() もそういう仕組みってこと?』
「そう、実際に見てみようか。 Spy++ 実行して」
『〈てすと〉ダイアログに照準合わせて【メッセージ】にして OK 押して
メニュー【メッセージ】−【オプション】で…… BN_ ってないよ?』
「通知メッセージは直接送られてくるわけじゃなくて、 WM_COMMAND や
WM_NOTIFY っていうメッセージの WPARAM に含まれてくるんです。今回は
WM_COMMAND を見てみて」
『 WM_COMMAND を表示するメッセージにして OK で、【参照】ボタン!』
<00007> 00000EC0 S WM_COMMAND wNotifyCode:BN_CLICKED wID:1003
hwndCtl:00000EDC
<00008> 00000EC0 R WM_COMMAND
『 BN_CLICKED ってのがそうなのね。例によって、 MFC がうまくこれを処
理して CFileTestDlg::OnBtnShow() を呼んでくれてるんだ』
「そういうこと。で」
『あ、その前にこれの読み方教えて』
「あ、えーっと、一番左の <00007> は表示してるメッセージの番号。
00000EC0 はメッセージを受け取ったウィンドウのハンドル」
『つまりダイアログのハンドルね』
「3番目の S は Send 、つまり送られてきたメッセージ。 R はウィンドウ
プロシージャでそれを処理したっていう意味の Receive 」
『ってことは S と R はペア?』
「そういうこと。あ、でも S じゃなくて Post の P もあるから。メッセー
ジ送る API には SendMessage() と PostMessage() の両方があるからね」
『それが区別されてるんだ』
「4番目は送られてきたメッセージ。それ以降は各メッセージごとに違うか
ら」
『じゃー WM_COMMAND のリファレンスっと。あ、また HIWORD ってわけわか
んないの出てきた』
「あ、じゃあついでにこれの意味も見ておこうか。 WM_MOUSEMOVE のメンバ
関数」
『はこの前削除しちゃったよ』
「あ! そういえば……いーや、面倒だけどもう一度メンバ関数作って」
『めんどくさ、行き当たりばったりでやってるからこういうことになるの
よ』
「う”」
『はい作ったよー』
「 CDialog::OnMouseMove() にブレークポイントをセットして、実行したら
……〈てすと〉ダイアログの右端からマウスカーソルを入れていって」
『ようするにダイアログの右端へんで WM_MOUSEMOVE が出るようにすればい
いんだよね。ほいブレークポイントに止まったよ』
「座標憶えておいて、あ、16進表示で」
『 x 座標が 0x00000168 、 y 座標が 0x00000036 だって』
「そしたらステップアウトを1回して、変数ウィンドウ表示して」
『中カッコから出るボタン、ウィンドウに四角が3つボタンっと』
「 WM_MOUSEMOVE のリファレンスにあるように、座標は LPARAM に入ってま
す」
『その LPARAM の値は…… 0x00360168 だって。あ!』
「そう、 LPARAM の最初の4バイトに y 座標が、後の4バイトに x 座標が
入ってるんです」
『そういえば、 WM_MOUSEMOVE のとこに書いてある』
xPos = LOWORD(lParam);
yPos = HIWORD(lParam);
『この LO とか HI とかっていうのが、低いと高いってことでしょ』
「そう、 WORD は API の型のひとつで unsigned short のこと。 LOWORD()
は4バイト整数型から下位2バイトを取り出すの、 HIWORD() は同じく上位
2バイトを取り出すもの」
『なんだ、仕組みがわかっちゃえば簡単ね』
「元の話に絡めると、 Ver 5.28 ( No.093 ) でウィンドウプロシージャを
見てもらったけど、あれで全部のメッセージを処理しなきゃいけないわけ」
『だから、ひとつの変数にふたつの値を詰め込むなんてことが必要なんだ』
「でもこれだと分かりにくいでしょ。わざわざ上位2バイト下位2バイト取
り出さなきゃいけないんだから」
『 MFC はその部分を隠して楽にできるようにしてくれてるってわけね』
「そういうこと。ウィンドウやコントロールの全体像はこんな感じ」
『……なんとなーく、分かったような気がする』
「うん、今はそんなとこでいいから」
『いいの?』
「こういうのは自分で組むようにならないと実感できない部分あるからね」
『 MFC みたいなの自分で作るの!?』
「いや、そこまではいかないけど、でも半年後位にはウィンドウプロシージャ
作ってもらうから」
『難しい?』
「そんなことないよ。ただ、先に憶えてもらいたいことがあるからちょっと
後回しってだけ」
『ホントに半年後にできるのかな……』
「あ、そうだ、さっきの CFileTestDlg::OnMouseMove() 削除しといてね」
『もっと計画性なんとかしろー!』
/*
Preview Next Story!
*/
『で、まだ Ver 5 は続くのね』
「次はファイルダイアログ!」
『ファイルダイアログ?』
「そう、〈ファイルを開く〉とか〈ファイルを保存〉とかのダイアログ」
『というわけで次回』
< Version 5.30 ファイルダイアログを使おう! >
「につづく!」
『げ、それあたしが作るの!? できるわけねーっ!!』
「いやだから、それを作らなくていいんだよって話だから」