- ベストアンサー
ボールの移動に関する問題
- InvalidateRectがうまくいかない問題について質問があります。
- 特定の条件で、ボールの移動が予想とは異なる挙動をするようです。
- デバッグを行った結果、ループとInvalidateRectは正常に実行されていることが分かりました。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
>UpdateWindow()がキューに入れないで直接プロシージャにWM_PAINTを送信するっていうのは >わかったのですがなぜUpdateWindow()だけだといけないのでしょうか? 「無効領域があった場合にWM_PAINTを送信」だからでしょう。 http://msdn.microsoft.com/ja-jp/library/cc428780.aspx に >指定されたウィンドウの更新リージョンが空ではない場合 とありますし。 無駄にWM_PAINTは抑える方向に考えられている。ということかと。 >WM_TIMER内でwhileを回していました。 メッセージを回すのが止まってしまうので出来れば避けた方がよいかと……。 スレッドにする。という方法があるかと思われます。 # 排他制御とか必要になりますが…。
その他の回答 (4)
- m-take0220
- ベストアンサー率60% (477/782)
ちなみに、WM_TIMERの処理を状態で分岐したいなら、グローバル変数など使わずに、nIDEventを利用するのが楽です。もしくは、別関数にしてSetTimerでlpTimerFuncに指定する方法もあります。
お礼
回答ありがとうございます。 タイマー用のプロシージャを作るということですかね。 当初はタイマー的な要素は1つの予定だったのでWM_TIMERで行なっていましたが それもいいと思いました。
- kngj1740
- ベストアンサー率18% (197/1052)
Sleep(100);は確かにWindowsに制御は返しますがWM_TIMERメッセージの処理から抜けているわけではないので、WindowsはWM_Paint処理を呼ぶことは出来ません。すなわち描画は更新されません。 WM_TIMERメッセージの処理の中でSleepするのではなく、グローバル変数に現在の状態を記憶し毎回WM_TIMERメッセージの処理を抜け、WM_TIMERメッセージの処理に入った時にグローバル変数の値によって処理を分岐するような形にする必要があります。いわゆる状態遷移表によるプログラミングになります。Windowsのプログラミングは面倒です。
お礼
回答ありがとうございます。
- m-take0220
- ベストアンサー率60% (477/782)
InvalidateRectは、ウィンドウの無効領域を更新します。その結果、ウィンドウに再描画が必要になった場合は、WM_PAINTメッセージが発行されます。ただし、すでにメッセージキューにWM_PAINTがある場合は、新たにWM_PAINTは発生しません。 ウィンドウの描画処理は、WM_PAINTメッセージを処理することで行われるので、whileループがメインスレッドにある場合、他のメッセージ処理を行う機会がないため、whileループが終了するまで画面が更新されません。 Sleepで時間稼ぎをすると、そのスレッドは停止状態になり、何も処理できなくなるので、少なくともメインスレッドでは使うべきではないと思います。SetTimerでは問題があるのであれば、少なくともSleepする前にUpdateWindowなどメッセージ処理以外の機会でウィンドウの更新をするべきでしょう。
お礼
回答ありがとうございます。 Sleep()の件考えてみます。
- Wr5
- ベストアンサー率53% (2173/4061)
複数のWM_PAINTは纏められる場合がありますけど…その辺はどうなんでしょう? InvalidateRect(hWnd, NULL, TRUE)で無効領域(再描画が必要な領域)が設定されて、 メッセージキューに積まれます。 メッセージキューにWM_PAINTが残っている状態でInvalidateRect(hWnd, NULL, TRUE)で 再度無効領域を設定すると、WM_PAINTはどっちか1つだけ…ということになるかと。 # いくつか進んでいるキューの途中のWM_PAINTが残って最新のInvalidateRect()分がキューに積まれないか、 # 途中のWM_PAINTがキューから削除されて、改めてキューの最後にWM_PAINTが入るか……。 InvalidateRect()の後にUpdateWindow()入れたらどうなります? 念のため確認ですが…そのwhileループってなんらかのメッセージ処理中(ボタンクリックとかWM_TIMERメッセージとか)で回していたりしないですよね? WM_TIMERのハンドラ内で回していると、WM_PAINTはまず間違いなく1つに纏められますよ。 # タイミングによっては「応答なし」がウィンドウタイトルに付くかも知れません。
補足
回答ありがとうございます。 WM_TIMER内でwhileを回していました。 InvalidateRect()はWM_PAINTメッセージをキューに入れるので 他のメッセージの最中に5回呼び出すっていうのは WM_PAINTの内容を実行させずただキューに5個入れるようとしちゃってたんですね。 実際は1つにまとめられるようですが。理解できました。 >>InvalidateRect()の後にUpdateWindow()入れたらどうなります? やってみたら5回更新されていました。 ただInvalidateRect()を消してみたら先ほどと同じ状態になってしまいました。 UpdateWindow()がキューに入れないで直接プロシージャにWM_PAINTを送信するっていうのは わかったのですがなぜUpdateWindow()だけだといけないのでしょうか?
お礼
ありがとうございます。勉強になりました。 スレッドの方向で考えてみます。