#pragma twice

KAB-studio > プログラミング > #pragma twice > 183 Version 10.05 デフォルトウィンドウプロシージャ

#pragma twice 183 Version 10.05 デフォルトウィンドウプロシージャ

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

 Version 10.05
デフォルトウィンドウプロシージャ

前回はメッセージループについて見てみました
ちょっと飛ばしてたけどね
あとでちゃんと復習するから大丈夫。で、今回は、メッセージループから
送られてきたメッセージの処理
つまりウィンドウプロシージャね
そゆこと。というわけで Version 10.03 ( No.181 ) で紹介したウィンド
ウプロシージャについてもう一度見てみます

// ウィンドウプロシージャ。
LRESULT CALLBACK WndProc
    ( HWND p_hWnd
    , UINT p_uiMessage
    , WPARAM p_wParam
    , LPARAM p_lParam
    )
{
    if( p_uiMessage == WM_DESTROY )
    {
        // ×ボタンが押されました。
        PostQuitMessage( 0 );
        return 0;
    }

    // 標準的な処理をします。
    return DefWindowProc( p_hWnd, p_uiMessage, p_wParam, p_lParam );
}

なんかかなりシンプルだね
 Version 8.10 ( No.152 ) とかのダイアログプロシージャに比べると、
ってこと?
そうそう。あのときとかメッセージいっぱいあったじゃない
もちろん、ウィンドウもいろんなメッセージを受け取るようにしたらああ
いふうにごちゃごちゃしてくるけど
そっか、そうなっちゃうんだね……あ、でも、なんかウィンドウ閉じるの
は面倒そうじゃないね
そうだね、あのときはボタンの ID とか調べたりしなきゃいけなかったか
らね。ウィンドウの場合、閉じるときには右上の×ボタンを押したとき、だ
から
ボタンの ID は関係ないわけね。で、実際に押されたときはどうなる
の?
そうだね……今回は色々実験してみようか。まず

// ウィンドウプロシージャ。
LRESULT CALLBACK WndProc
    ( HWND p_hWnd
    , UINT p_uiMessage
    , WPARAM p_wParam
    , LPARAM p_lParam
    )
{
    // 標準的な処理をします。
    return DefWindowProc( p_hWnd, p_uiMessage, p_wParam, p_lParam );
}

お、なんかえらいシンプルに!
これで実行して、ウィンドウ閉じてみて
ほい。あれ? ちゃんと閉じたよ
よく見てみて
へ? ちっちゃいウィンドウでも残ってる?
いや、そうじゃなくて……アプリ自体は終了してないでしょ
あ! ホントだ、 VC がまだデバッグモードになってる
つまり、ウィンドウプロシージャの

    if( p_uiMessage == WM_DESTROY )
    {
        // ×ボタンが押されました。
        PostQuitMessage( 0 );
        return 0;
    }

の部分は、ウィンドウを閉じるためじゃなくて、ウィンドウが閉じたとき
にアプリを終了させるための部分なんです
おー
ウィンドウが閉じたときには WM_DESTROY が送られてきます。そこで 
PostQuitMessage() を呼び出します
あ、このへんは前回やったね
そう、ここからはメッセージループの話。この API を呼ぶと WM_QUIT が
メッセージループに送られて、それを GetMessage() が受け取ると
 0 を返して、それが if にひっかかって抜けるわけね
そゆこと。ちなみにこの PostQuitMessage() に渡した数字が、 WM_QUIT 
の WPARAM に入れられます
ん? どゆこと?
ほら、前の最後で

    return stMsg.wParam;

あー、これが WM_QUIT の WPARAM で、つまり PostQuitMessage() の引数
なんだ
ってことは、 WinMain() の戻り値は PostQuitMessage() に渡した値って
こと。まぁ WinMain() の戻り値自体はあまり意味ないけど、ここでその戻
り値を決められるってことで
ウィンドウでエラーが起きたときとか、ここに違う数字渡せばいいのね
そゆこと。で、話を戻すと
ようするに、あの部分はウィンドウ閉じるのとは無関係、と
そうそう、つまりウィンドウを閉じること自体は自動だけど、ウィンドウ
が閉じたときにアプリを閉じさせるのは、実際にコードとして書かなきゃい
けないってこと
めんどいねー
確かに簡単なアプリならそうだけど、たとえばウィンドウをいっぱい作る
アプリなら
そっか、そういうときだと、今表示されてるウィンドウがいくつあるとか
調べて、全部なくなったときだけアプリを終了する、みたいな?
そういう処理が必要な場合もあるからね。だから、この辺は自動じゃなく
て手動になってるわけ
わからなくもないけど、めんどいのはめんどい……
まーね。でも、ウィンドウを閉じるのは自動だったでしょ?
あ、そういえば
そういう部分もあるってこと。今度はその部分を見てみます。ウィンドウ
が自動的に閉じるのは、ずばり、この API のおかげです

    return DefWindowProc( p_hWnd, p_uiMessage, p_wParam, p_lParam );

そういえば最後にこの関数を呼んでるよね
この API は【デフォルトウィンドウプロシージャ】って言います
デフォルトはあのデフォルトだよね
そう。つまり〈標準のウィンドウプロシージャ〉ってこと。ウィンドウっ
て、実はものすごく膨大な量のメッセージが大量に送られてきてます
 Version 5.28 ( No.093 ) で見たよ
そうそう、 Spy++ で見れば分かるけど、ものすごい量のメッセージがど
んどん送られてくるんです。で、そのすべてに対応してたら大変だから、標
準的な処理でいいメッセージは、そのままデフォルトウィンドウプロシー
ジャに渡しちゃうんです
それがさっきの呼び出してる部分? そういえば、引数とか戻り値とか全
部同じなんだね
そりゃ、ウィンドウプロシージャなんだから。デフォルトウィンドウプロ
シージャの中にはそれぞれのメッセージに対応した標準的な処理をしてくれ
ます。逆に言うと……

// ウィンドウプロシージャ。
LRESULT CALLBACK WndProc
    ( HWND p_hWnd
    , UINT p_uiMessage
    , WPARAM p_wParam
    , LPARAM p_lParam
    )
{
    return 0;
}

呼んでないと……あ、ウィンドウが作られもしない
でしょう。そういう初期化部分も肩代わりしてくれてるから
特にメッセージを受け取らなくても表示できちゃうわけねー
で、その標準的な機能のひとつに、ウィンドウを閉じる、というのがあり
ます
だから勝手に閉じてくれるんだね
というわけで、今度は閉じさせないようにしてみます。ウィンドウを閉じ
たときには WM_CLOSE ってメッセージが送られてきます
そのまんまだね
で、これをデフォルトウィンドウプロシージャに渡さないようにしてみます

// ウィンドウプロシージャ。
LRESULT CALLBACK WndProc
    ( HWND p_hWnd
    , UINT p_uiMessage
    , WPARAM p_wParam
    , LPARAM p_lParam
    )
{
    if( p_uiMessage == WM_DESTROY )
    {
        // ウィンドウが閉じました。
        PostQuitMessage( 0 );
        return 0;
    }
    else if( p_uiMessage == WM_CLOSE )
    {
        // ×ボタンが押されました。
        return 0;
    }

    // 標準的な処理をします。
    return DefWindowProc( p_hWnd, p_uiMessage, p_wParam, p_lParam );
}

 WM_CLOSE を受け取ってそこでウィンドウプロシージャから抜けるわけ

とりあえず試してみて
ほい。お、閉じなくなった!!
デフォルトウィンドウプロシージャに WM_CLOSE が渡されないから、ダイ
アログが閉じないんです
つまり DefWindowProc() に WM_CLOSE を渡すとウィンドウが閉じるって
ことねー
そういうこと。さらに、ウィンドウが閉じるってことは、そのときに一緒
に WM_DESTROY が送られてきます
あ、そっか! それを受け取って、アプリを終わらせるわけね
そゆこと。順番的には……

1:ウィンドウを閉じる(xボタンを押す)。
2: WM_CLOSE が送られてくる。
3: DefWindowProc() が受け取ると、ウィンドウを閉じる。
4:ウィンドウが閉じられると、 WM_DESTROY が送られる。
5:それを受け取って PostQuitMessage() を呼び出す。
6:メッセージループから抜けて、アプリが終了する。

という感じになります
ふ、複雑……
確かにねー。ウィンドウを閉じる部分と、アプリを終了する部分とが別々
で、しかも関連してるからね
……そういえば、 WM_DESTROY のコメントが違ってるね
うん、変えました。単純に、ウィンドウが閉じたときにアプリを終了させ
る、ってことなら WM_CLOSE のことは気にする必要ないでしょ。それなら
 WM_DESTROY が〈×ボタンを押したとき〉なわけね。でも正確には〈ウィ
ンドウを閉じたとき〉なのよねー
このへんは複雑だから、プログラムを色々試したり、図を書いたりして頭
の中でイメージできるようにね

/*
    Preview Next Story!
*/
でもさ、イメージってできてなきゃいけない?

アクセル踏むときに、車の仕組みを考える必要はないでしょ?
そりゃ乗る人はね。作る人は必要でしょう
必要? 車を作る人が、エンジンの仕組みとかまで知ってなきゃダメ?
知ってなきゃだめ……だと思う
そうかなー
というわけで次回
< Version 10.06 キー入力メッセージの置き換え >
につづく!
だから教えてくから
おうぼうー
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。