#pragma twice

KAB-studio > プログラミング > #pragma twice > 182 Version 10.04 メッセージループ!

#pragma twice 182 Version 10.04 メッセージループ!

前のページへ 表紙・目次へ 次のページへ

 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 から始めたのって良くなかったの?
……
げっ
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。