プログラム走らせたら、Windowsが制御不能に

解決


猫丸  2003-04-26 01:28:30  No: 51331

Windowsの描画回りを調べるために組んだ自分のコードのチェックの為に、しばらく作ったアプリを走らせてたらGUIが暴走して、最後にPCが再起動しました(涙
描画の高速処理の為に、イメージとして、

case WM_CREATE:
    SetTimer(hWnd,IDD_TIMER,1,NULL);

case WM_TIMER:
    InvalidateRect(hWnd,&rc,false);

case WM_DESTROY:
    KillTimer(hWnd,IDD_TIMER);

という感じのコードを組んだんですが、暴走したアプリの動きを見た感じでは、
そのアプリを終了させてもWM_PAINTの再描画要求が続いてると言うか、
他のアプリを起動した時も問答無用でクライアント領域の再描画をし続けてると言うか…(汗
制作したアプリはWin32 Applicationを選んで非常に短いコードで組んでいて結局何が原因なのか解らなかったので、こちらに書き込む事にしました。
どなたか助けてください。
ちなみに、こちらの参考文献は、
「C/C++プログラマのためのWindows95プログラミング」
です。
基本的には同じWin32だからという理由です。


瀬戸っぷ  2003-04-26 02:01:00  No: 51332

WM_PAINTの応答でBeginPaint()/EndPaint()を正しく処理していますか?
処理していない場合、WM_PAINTがポストされ続けますが。


猫丸  2003-04-29 01:45:17  No: 51333

正しく処理しているかと聞かれると「むぅ」となりますが、
Win32 Application側で入れてくれているBegin、EndPaintをそのまま流用してます。

case WM_PAINT
    hdc = BeginPaint (hWnd, &ps);
    // TODO: この位置に描画用のコードを追加してください...

    『ここに私の描画系の処理が入ってます』

    EndPaint( hWnd, &ps );
    break;

うーん、そうなんです。
たぶん十中八九WM_PAINTがポストされ続けてるんです。
で、なんでポストされ続けてるのかが解らんのです…。

>WM_PAINTの応答でBeginPaint()/EndPaint()を正しく処理していますか?

Windowsの描画系はWM_PAINTの応答でBeginPaint()/EndPaint()の処理を仕損なう以外にWM_PAINTがポストされ続けるケースと言うのはあるのでしょうか?
無いのなら、原因が絞れて有り難いのですけど。


瀬戸っぷ  2003-04-29 03:14:53  No: 51334

>Win32 Application側で入れてくれているBegin、EndPaintをそのまま流用してます。

「標準的な"Hello World!"アプリケーション」の場合ですね。
BeginPaint()/EndPaint()が呼ばれているので、
無効領域のクリアが問題ではないようですね。

とりあえず、
SetTimer(hWnd,IDD_TIMER,1,NULL);
1ms間隔のタイマーでは早すぎると思われます。
NT系でデフォルトの場合、タイマー精度は5msか10ms程度だったハズです。
(9x系の場合は1msですが、ソレでも早すぎます。)
リフレッシュレート60Hzで16.6666…msですから。
また、タイマーの破棄もWM_CLOSE(内のDestroyWindow()より前)でやった方がいいと思われます。
こちらは試していないので不明ですが…

>Windowsの描画系はWM_PAINTの応答でBeginPaint()/EndPaint()の処理を仕損なう以外に
>WM_PAINTがポストされ続けるケースと言うのはあるのでしょうか?

無いと思いますが断定は出来ないかと………
ありがちなのが、デバッグ実行していてウィンドウが重なっている時とか……
(ブレークポイントでVSがアクティブに、ステップ実行してアプリがアクティブになった時に諸々のメッセージが…など)


ブタゴリラ  2003-04-30 22:32:05  No: 51335

case WM_TIMER:
    InvalidateRect(hWnd,&rc,false);

が原因ですね。
再描画メッセージを送ればそりゃWM_PAINTが呼ばれますよ。(1ミリ秒単位で)
(rcの中身にもよるかもしれないけど。)


ブタゴリラ  2003-04-30 22:38:33  No: 51336

ちょっと手違いがあったので、修正。
WM_PAINTは他にメッセージキューが無くて、更新領域があった場合に呼ばれるので、1ミリ秒単位ではありません。失礼しました。

後、原因として考えられるのは、

//////////////////////////////////
case WM_CREATE:
    SetTimer(hWnd,IDD_TIMER,1,NULL);

case WM_TIMER:
    InvalidateRect(hWnd,&rc,false);

case WM_DESTROY:
    KillTimer(hWnd,IDD_TIMER);
//////////////////////////////////
breakが足りませんね。(^^;(ただの省略だと思いますけど・・・。)


猫丸  2003-05-02 11:16:10  No: 51337

いろんなレスを皆さん有り難うございます。
breakは省略してます(申しわけないっす。情報足りませんね(−−;)

>1ms間隔のタイマーでは早すぎると思われます。
>NT系でデフォルトの場合、タイマー精度は5msか10ms程度だったハズです。
>(9x系の場合は1msですが、ソレでも早すぎます。)

Windowsのタイマー精度が高くない事は知ってたんですが、
とりあえず現時点では「描画の高速処理だ。おぉ〜!」という感じで、
一番遅くならなそうな数値を入れてます。
だけど、「画面との同期」の存在すら忘れてたので、
言われてみると確かに速い、速すぎる…(−−;
あと、WM_PAINTの処理のされ方の関係で、WM_PAINTは確か処理出来るタイミングでWindows側が処理する様な事が何処かに書いてあったと思ったので、何はともあれ処理してくれる様に振った数値という事でもあります。
ただ、Windows側がどこまでメッセージをプールしてくれるのか…なるほど、メッセージキューが足りなくなって暴走してるんだろうか。

>case WM_TIMER:
>    InvalidateRect(hWnd,&rc,false);

>が原因ですね。

WM_PAINTを高速に発行するためにこういう書き方になったんですが、マズいですかね?(−−;
KillTimerでタイマーが終了すると、WM_PAINTの発行も終了すると言う図式を書いたんですが、これ以外にスマートな書き方思い付かなくて…。
ところで、メッセージキューを消化せずにアプリが終了する事ってあるんですか?
PostQuitMessageはWM_QUITメッセージをポストしますけど、メッセージキューに大量のWM_PAINTが先客でいたら…(−−;;

なんとなく問題の本質が見えてきました。
まだ「これだっ!」というものは無いんですけど、WM_PAINT回りがとにかく雑なんですね。
あと、終了回りもチェックしてみます。


猫丸  2003-05-02 12:43:58  No: 51338

追伸
>「標準的な"Hello World!"アプリケーション」の場合ですね。
その通りです(−−;


猫丸  2003-05-02 13:31:57  No: 51339

問題がどうも解決したんです、が…(−−;

何故にこうも歯切れの悪い書き方になるかと申しますと、
アプリが暴走していた原因がWM_PAINT系に無かった様で…(−フ−;
それで、新たな疑問が一つ浮上したのでそれを書きます。
これが解決するとWindowsの描画回りが何となく解った気がするので。

まず、WM_PAINTの中身やWM_TIMERを私が組んだコードのアルゴリズムのまま変更していない状態で、つまり、

case WM_CREATE:
    SetTimer(hWnd,IDD_TIMER,1,NULL);

case WM_TIMER:
    InvalidateRect(hWnd,&rc,false);

case WM_DESTROY:
    KillTimer(hWnd,IDD_TIMER);

case WM_PAINT
    hdc = BeginPaint (hWnd, &ps);
    // TODO: この位置に描画用のコードを追加してください...

    『ここに私の描画系の処理が入ってます』

    EndPaint( hWnd, &ps );

「breakは省略してます(笑」
で、最初私は自分のアプリを組む上で必要なコード、

HBRUSH br;    ←グローバル変数

case WM_PAINT
br1 = CreateHatchBrush(HS_CROSS,0x00000000);
      br2 = CreateSolidBrush(0x00000000);


猫丸  2003-05-02 13:48:51  No: 51340

…間違えて、送信ボタンを押してしまった(涙
続きです…。

case WM_PAINT  の中に、
      br = CreateHatchBrush(HS_CROSS,0x00000000);
      FillRect(memdc,&rc,br);
      br = CreateSolidBrush(0x00000000);
      FrameRect(memdc,&rc,br);

と書いていたんですが、描画を続けているとブラシ設定がおかしくなってたので、
頭にCreateと付く関数をWM_CREATEに移動しました。
そしたら、アプリが何の問題も無く動作するように。
つまり、私は描画途中でブラシが壊れてしまう現象をWM_PAINTの暴走と捉えていた訳なんですが、ここで大きな疑問が一つ。
何故にWM_PAINTでは駄目なのか。
WM_TIMERにこの処理を移してもやはりブラシは壊れる。
謎だ…(−−;

何にしても、発生していたクリティカルな問題は解決したので感謝。


YuO  2003-05-02 19:06:55  No: 51341

単に,ブラシを削除しないからリソースリークが起きたのでしょう。
ブラシをちゃんと削除してやれば問題は起きません。


ブタゴリラ  2003-05-02 22:07:24  No: 51342

そうですね。
      br = CreateHatchBrush(HS_CROSS,0x00000000);
      FillRect(memdc,&rc,br);
      br = CreateSolidBrush(0x00000000);
      FrameRect(memdc,&rc,br);
の最後の行に、

      DeleteObject(br);

をつけてやれば、いけると思います。

では、頑張ってください。


猫丸  2003-05-06 10:38:47  No: 51343

レス有り難うございます。
目から鱗が落ちました。
なんか、もーれつに感動してます(;フ;

全ての謎が繋がりました。
オブジェクト系のプログラミングで作ったオブジェクトは最後に破棄するという鉄則を私はまた忘れてたんですね(−−;
いえ、実は私はメモリを確保した後の破棄(解放)を忘れるというミスをよくするんですが、今回もリソースを取り続けてた訳で…。
PCのリソースが有限である事は当たり前の事で、その件で他人に助言をする事もよくある事なんですが、リソース管理を自動化する最近の高級言語の考え方とかごっちゃになって自分でコード書いててスコーンと頭から抜ける事のなんと多い事か(−−;

Create→Delete→(−−;その通りだよ、自分

また何か解らない事があったら書く事もありますが、
この件は完全に解決しました。
どうも、ありがとうございました。
がんばりまっす。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加