GetMessageの仕様についてPart2

解決


匿名希望  2005-05-31 09:47:24  No: 57624  IP: 192.*.*.*

先日GetMessageに関して少し質問させて頂いたのですが、
仕様が未だよくわかっておらずまたここにおせわになってしまいました^^;

while(Msg.message != WM_QUIT){
  if (GetMessage(&Msg, NULL, 0, 0)){
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
  }
}

以上のソースについてなのですが、これではメッセージは受け取るものの
またもや激しく処理落ちしてしまいます。

以前、
if (GetMessage(&Msg・・・){
の文についてBanさんよりこれではエラー時に具合が悪いとご指摘頂いたのですが
その詳細がよくわかりません(´・ω・`)

自分的にはこのGetMessageのif文によりTRUE時(メッセージ取得時)にTranslate&Dispatchが実行され、FALSE時(WM_QUITorエラー)にはそれらを実行しないものだと認識しているのですがどこが悪いのか詳細を教えて頂けたらと思います。(力不足で申し訳ないです)

また、PeekMessageとGetMessageの違いについてもなのですが、
メッセージ取得時に待機するかそうでないかという違いについては理解できたのですが、(つもりかも・・・)その詳しい詳細がやはり・・・

if (PeekMessage(&Msg, NULL, 0, 0, PM_NOREMOVE)){    
  if (GetMessage(&Msg, NULL, 0, 0)){
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
  }
}

以上の文についてPeekMessageでメッセージを取得し、そのメッセージをキューに残したままGetMessageでさらにメッセージを受け取るということが二度手間ではないかと思い、GetMessage文を削除し、PeekMessageでメッセージを受け取りPM_REMOVEでTranslate&Dispatchしてみようと試みましたがやはりだめでした。
(メッセージを受け取らずキー操作等を認識せず)
また、それをPM_NOREMOVEにしてみるとうまくいきました。
なぜPeekMessageにてメッセージを受け取り、それを引数として渡しているのにうまくいかないのか、そして以上の理由によりPeekMessageを削除し、GetMessageのみにすると処理落ちしてしまうのか、(↑で最初に書いた内容です)
どうかご指導頂きたく。(´Д⊂

尚、以前Banご指摘の通りPeekMessageではCPUを喰うためできるだけ今回のPGには使いたくないと思います。

編集 削除
匿名希望  2005-05-31 10:04:22  No: 57625  IP: 192.*.*.*

追記です。

すいません。さらに教えて下さい。
↑の文のPeekMessage後のGetMessageについてなのですが、
以前の、
タイトル:GetMessageにかかる負担について〜
の、Banさんとのやりとりを見て頂くとわかりやすいかと思うのですが、
最初自分はPeekMessageにてPM_REMOVEを指定いたため、メッセージが破棄されてしまいGetMessageできちんとメッセージを取得できていませんでした。

それをPM_NOREMOVEにすることによってPeekMessageでメッセージを確認した後に、
GetMessageにてきちんとメッセージを確保することができたのですが、
起こっていた事象が処理落ちするというものでした。

PeekMessageでメッセージを破棄してしまってGetMessageがメッセージを受け取れなかったのならば処理される内容等なく、処理落ちすることなどないのでは?
と、思います。
改善法はわかったのですが、結局自分なりに原因がわからなかったので追記させて頂きました。
長々ともうしわけないです。

編集 削除
シャノン  2005-05-31 10:13:42  No: 57626  IP: 192.*.*.*

> 自分的にはこのGetMessageのif文によりTRUE時(メッセージ取得時)に
> Translate&Dispatchが実行され、FALSE時(WM_QUITorエラー)にはそれらを
> 実行しないものだと認識しているのですがどこが悪いのか詳細を教えて
> 頂けたらと思います。(力不足で申し訳ないです)

エラー時には -1 が返ります。
FALSE は 0、TRUE は 0 以外のすべてですので、-1 は TRUE と見なされ、ループが継続されてしまいます。
…という解説は MSDN の GetMessage の項にも書いてあるのですが、未だに VC++ の Win32 AppWizard はこういうソースを書いてくれます。困ったもんだ。

> GetMessage文を削除し、PeekMessageでメッセージを受け取りPM_REMOVEで
> Translate&Dispatchしてみようと試みましたがやはりだめでした。
> (メッセージを受け取らずキー操作等を認識せず)

できましたヨ?
少なくとも WM_CHAR は認識しましたが。

強いて違う点を挙げるとするならば、第2引数に NULL を指定すると、GetMessage は PostThreadMessage で投げられたメッセージも取得するのに対し、PeekMessage はウィンドウにポストされたメッセージしか扱わず、スレッドにポストされたメッセージも扱いたい場合は -1 を指定しなければならない、ということくらいでしょうか。

編集 削除
Ban  2005-05-31 10:37:29  No: 57627  IP: 192.*.*.*

説明は既にシャノンさんがしてくださっているので省略。

> <MSDN>
    -- snip --
> エラーが発生した場合、-1 が返ります。
> </MSDN>


> PeekMessageでメッセージを破棄してしまってGetMessageがメッセージを
> 受け取れなかったのならば処理される内容等なく、
> 処理落ちすることなどないのでは?
> と、思います。

入ったばかりのメッセージを PeekMessage で破棄してからGetMessage を呼ぶと、
(当然ながら)新しいメッセージが届くまで GetMessage が更にブロッキングします。

おそらく 匿名希望 さんのコードでは、この合間で画面更新を回していると
思われるため、GetMessage がブロッキングすると、その間画面更新処理が
走らなくなり、結果としてフレーム落ちという現象が出たのではないかと
推測しています。

編集 削除
匿名希望  2005-05-31 11:22:12  No: 57628  IP: 192.*.*.*

さっそくの回答ありがとうございます。

返答遅れて申し訳ありません。

シャノンさんBan大変お世話になっております。

おかげさまで頭の中が整理できました^^

これで解決とさせて頂きます。

また何かありましたらよろしくお願い致します。

if文については初歩的なことを忘れてました・・・
申し訳ないです。

編集 削除
うぃろ  2005-05-31 22:28:00  No: 57629  IP: 192.*.*.*

特にこの問題と関連しているわけではないのですが、
自分もゲームを作成していて気になったので書き込みします。
(もう解決しているようなので、参考程度に?)

ゲームを作成する場合、通常ゲームメイン処理とメッセージ処理を分けている(と思う)ため、
GetMessageを使うとメッセージを受け取るまで処理がブロッキングされてしまい
ゲームメイン処理がストップしてしまいます。
なので、ゲームを作成する場合にはメッセージループを以下のように書き換えると、どこぞで紹介されていた記憶があります。

do {
    if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
    {
        // メッセージ処理
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else {
        // ゲームメイン処理
    }
} while( msg.message != WM_QUIT );

編集 削除
匿名希望  2005-05-31 22:53:40  No: 57630  IP: 192.*.*.*

うぃろさん参考ありがとうございます。

たしかにGetMessageはリアルタイムに処理する分には具合悪そうですね^^;
ちょっと皆さんの意見を元にループ処理を書き直したりしました。

現在は快適に稼動しています。
感謝感謝。

編集 削除
Ban  2005-05-31 23:45:14  No: 57631  IP: 192.*.*.*

エラー処理は省略されてるとして、やっぱり CPU 負荷は高いままですね。

フレームレートを稼ぐ上で仕方ないこともあるかもしれませんが、
(本当にそのレベルでないとどうしようもないほど重いゲームも多々あります)
ただ、可能ならゲームメイン処理の中で ::Sleep(1)をはさむとか、
MsgWaitForMultipleObjectsEx でタイムアウトつきのウェイトするとか、
そういう配慮が欲しいかも。

# レートに余裕があれば、単純に WaitMessage でもいいんでしょうが。

編集 削除
匿名希望  2005-06-01 12:07:22  No: 57632  IP: 192.*.*.*

ご指摘ありがとうございます。

一応配慮としてSleepを入れています。

WaitMessageだと待機してしまうんですね^^;

リアルタイムに処理する文でこんなに負荷がかかっていたとは思ってなかったです。
もっと効率いい方法があればいいんですが・・・。

編集 削除