Version 10.04
メッセージループ!
「前回はメッセージループのコードを紹介したところで終わりました」
『とりあえずビルドして実行! お、ちゃんと動く! ウィンドウも閉じ
る!』
「というわけで、 MFC を使わない場合はここまで書かないとウィンドウを
作ることができないわけです」
『……無茶苦茶めんどいね』
「ま、実際には MFC 使うことの方が多いからあんまり気にしなくていいん
だけど、それでも一応、この辺の仕組みは理解しておかないと」
『 MFC の仕組みがわからない?』
「そゆこと。さて、では前回のコードを踏まえて、ひとつひとつ見ていきま
す。まずは WinMain() から」
『最後に1行だけ』
return MessageLoop();
『ってしたんだよね』
「そう。 WinMain() でウィンドウクラスの登録をして、ウィンドウを作っ
て、その最後で」
『これで return してるってことは、この MessageLoop() って関数から
戻ってきたら、もうアプリが終わるってことだよね』
「そう。後で説明するけど、この関数、あ、 API だけど、これはウィンド
ウが閉じたときに戻るようになってます。だから、ウィンドウが閉じても
アプリを終了させたくないときは」
『ここを変えればいいわけだ』
「こういう、かなり根本の部分まで触れるのが、 MFC を使わないでプログ
ラムを組むときのメリットかな」
『その分、かなり面倒だけどね……』
「さて、ではその MessageLoop() の中身について見ていきます。まず、最
初にこういう構造体を用意してます」
MSG stMsg;
「 MSG 構造体、この中にはメッセージの情報がそのまま入ってます」
『かなりそのまんまね』
「次は while ループ」
『 1 が入ってるって事は、無限ループ?』
「そう、中の if にひっからない限りずーっと回り続けます」
『ずーっと……』
「このループが【メッセージループ】の本体です」
『あ、メッセージのループだからメッセージループ……』
「このメッセージループでは、まず最初に」
bRes = GetMessage( &stMsg, NULL, 0, 0 );
「でメッセージを取り出します」
『メッセージを……取り出す?』
「アプリを実行すると、アプリの中に【メッセージキュー】と呼ばれるもの
が作られます」
『めっせーじきゅー???』
「 Queue っていうのは順番待ちの列のこと。このメッセージキューには、
メッセージが順番通りに列を作って並ぶんです」
『???』
「たとえば、マウスを動かしたときのメッセージはなんだった?」
『えっと、 WM_MOUSEMOVE 』
「そう、だからマウスを動かすと、メッセージキューに WM_MOUSEMOVE メッ
セージがひとつ入れられます」
[メッセージキュー]
WM_MOUSEMOVE
「続いて、マウスの左ボタンを押したときは?」
『 WM_LBUTTONDOWN 』
「だから、今度は WM_LBUTTONDOWN がメッセージキューに入れられます」
[メッセージキュー]
WM_MOUSEMOVE
WM_LBUTTONDOWN
「同じく、ボタンを上げた時は」
『 WM_LBUTTONUP が入るんだ』
[メッセージキュー]
WM_MOUSEMOVE
WM_LBUTTONDOWN
WM_LBUTTONUP
『メッセージがどんどん貯まってくわけね』
「そう、取り出されない限りメッセージはメッセージキューの中に入ったま
ま。順番を守って行儀良く待ってます」
『取り出す……? ウィンドウプロシージャに行くんじゃないの?』
「そう、実はウィンドウプロシージャへは手動で送られるんです」
『しゅ、手動?』
「手動ってのはちょっと変か。プログラムとして作る必要があるってこと。
さっきの」
// メッセージをキューから取り出します。
bRes = GetMessage( &stMsg, NULL, 0, 0 );
「で、まずメッセージをひとつ取り出します。取り出すのは、一番最初に
キューに入ったメッセージ。だから、さっきのだと」
『 WM_MOUSEMOVE !』
「ということになります。 GetMessage() を呼ぶと、このメッセージの情報
が stMsg の中に入れられてきます」
『メッセージの情報……お! MSG のリファレンスに wParam や lParam が
ある! ってことは、これでマウスの位置がわかる!』
「 Version 8.08 ( No.150 ) でやったね」
『ここでメッセージを取り出したときに、もうそういう情報も一緒に取って
来ちゃうわけねー』
「で、取り出されたからメッセージキューは」
[メッセージキュー]
WM_LBUTTONDOWN
WM_LBUTTONUP
「ってなります」
『 WM_MOUSEMOVE はなくなっちゃったわけね』
「で、そのあとの if と」
// メッセージを変換します。
TranslateMessage( &stMsg );
「は後回し」
『あらま』
「直接は関係ないところだから。で、」
// ウィンドウプロシージャに送ります。
DispatchMessage( &stMsg );
「の部分が重要。この DispatchMessage() にさっき取り出したメッセージ
を渡すと、適切なウィンドウプロシージャに送ってくれます」
『適切って?』
「実は、メッセージキューにはそのアプリの中のいろんなウィンドウのメッ
セージがごちゃまぜになって送られてくるんです」
『げ! なにそれ!』
「そのまま処理するともう大変だから、 DispatchMessage() に渡して、実
際にメッセージを受け取るべきウィンドウプロシージャに送ってもらうんで
す」
『で、このメッセージは Version 10.03 ( No.181 ) のウィンドウプロシー
ジャに送られてくるわけね』
「そういうこと。具体的に言えば、 WndProc() が呼び出されて、その引数
に、 stMsg に入ってる値が渡される、ってことだけどね」
『ほー』
「で、 DispatchMessage() は、ウィンドウプロシージャでの処理が終わる
まで待ってます」
『ってことは、ひとつのメッセージが処理されてる間は、メッセージループ
は止まってるんだ』
「そゆこと。処理中なのにどんどんメッセージを取ってったら大変だから
ね」
『ウィンドウプロシージャから抜けると DispatchMessage() に戻ってき
て……あ、ループだから最初に戻るんだ』
「で、またメッセージを取ってきて、っていうのをずーっと続けるわけで
す。つまりメッセージループは」
1: GetMessage() でメッセージキューからメッセージを取り出す。
2: DispatchMessage() でウィンドウプロシージャに送る。
3:ウィンドウプロシージャでメッセージを処理する。
4:処理が終わると DispatchMessage() から返ってくる。
5:繰り返し。
『これが永久ループ、メッセージループなわけなのねー。でもずーっとじゃ
ないんでしょ?』
「もちろん。それじゃアプリが終了しないからね。その辺はこの部分」
if (
( bRes == 0 )
||
( bRes == -1 )
)
{
// 終了するのでループから抜けます。
break;
}
『 bRes は GetMessage() の戻り値ね』
「 -1 が帰ってきたときは GetMessage() がエラーの時。でもまずないか
な。それよりも 0 の時。これは、メッセージキューから取り出したメッ
セージが WM_QUIT の時だった場合」
『なに、このメッセージ』
「これは終了時のメッセージ。あとで詳しく説明するから簡単にだけど、
ウィンドウプロシージャの中に」
PostQuitMessage( 0 );
「ってあるでしょ。この API を呼ぶと WM_QUIT が送られるんです」
『つまり PostQuitMessage() を呼べば GetMessage() が 0 を返す』
「結果的にはね。ちなみに PostQuitMessage() はウィンドウが閉じたとき
に呼ぶようにしてます」
『ウィンドウが閉じたときに GetMessage() が 0 を返す』
「で、この if を見れば分かるように 0 の時には break するから」
『あ、メッセージループから抜ける、ってことはアプリが終了する!』
「そゆこと。つまり」
『ウィンドウを閉じるとアプリが終了する』
「という仕組みになってるわけです」
『……ちょっと整理するね』
1:ウィンドウを閉じる。
2: PostQuitMessage() を呼ぶ。
3: WM_QUIT が送られる。
4: GetMessage() がそれを取り出すと 0 が返ってくる。
5: 0 が返ってくると if に引っかかってループから抜ける。
6:メッセージループから抜けるとアプリが終了する。
『……複雑……』
「逆に言うと、この流れをひとつでも絶ち切っちゃうと、もうそこでアプリ
は終了しなくなっちゃいます」
『たとえば…… PostQuitMessage() を呼ばないようにしたり?』
「そゆこと。あ、最後に MessageLoop() の最後で」
return stMsg.wParam;
『これはメッセージループから抜けたときの戻り値で、……これが
WinMain() の戻り値にもなってるんだよね』
「 WinMain() の戻り値については Version 8.05 ( No.147 ) を参照。で、
じゃあこの wParam を返してるのはどんな意味があるのか、ってことは次回
に持ち越し」
/*
Preview Next Story!
*/
『次回は前にほっぽっといたウィンドウプロシージャね』
「 API だけだと、最初に作らなきゃいけないのが多くて……」
『だから細かい部分が後回しなわけね』
「かといって MFC がいいわけでもないんだけど」
『自分が作ってないってだけだものね』
「というわけで次回」
< Version 10.05 デフォルトウィンドウプロシージャ >
『につづく!』
「ま、どっちがいいかっていうのは微妙かもねー」
『げ、じゃあ MFC から始めたのって良くなかったの?』
「……」
『げっ』