none
無限ループのプログラムで、フォームのコントロールが消えます。 RRS feed

  • 質問

  • VB6.0のフォームから、Vidual Studio 2008のC++のDLLの無限ループの関数を呼び出しています。
    この時、フォームのコントロールがフォームの背景で消され見えなくなります。
    別のフォームで覆い被さったりすると起こり易いですが、まれに何にもしていなくても起こる場合があります。
    また、スクリーンセーバーに行き戻って来た時にもなります。この時はスクリーンセーバーの時の画面が残る場合もあります。(他のプログラムのフォームはチャンと描画されます)

    Vista グラフィック: ATI(TM) Radeon(R) HD 2400 XT 256MB は問題ありません。
    XP   グラフィック: オンボードのビデオコントローラー(Intel Q43)     が問題です。

    OSの違いによるものでしょうか?
    何らかのグラフィックボードが必要なんでしょうか?
    その他に原因があるものでしょうか?
    プログラムによる何らかの対応がありませんか?

    何か意見をお聞かせ願えればと思います。

    2010年4月23日 5:15

回答

  • コントロールの描画を行っているスレッドが、「無限ループの関数」の中に入ったまま、描画処理を行わなければコントロールの再描画は行われないためにそんな動作になっているような。

    その、「無限ループの関数」から返ってこないのであればその作りが悪いと思います。
    (常時DLL側で処理がしたいのであれば、DLL側でスレッド起動してそちらで処理をさせるとか・・・)

    • 回答としてマーク クサキ 2010年4月28日 6:15
    2010年4月23日 5:34
  • VB6.0のフォームから、Vidual Studio 2008のC++のDLLの無限ループの関数を呼び出しています。
    この時、フォームのコントロールがフォームの背景で消され見えなくなります。
    別のフォームで覆い被さったりすると起こり易いですが、まれに何にもしていなくても起こる場合があります。
    また、スクリーンセーバーに行き戻って来た時にもなります。この時はスクリーンセーバーの時の画面が残る場合もあります。(他のプログラムのフォームはチャンと描画されます)

    Windows のプログラムはそんなものです。
    ほかのウィンドウで上書きされたり、コンピュータのロックでかき消されたりした場合は、画面に書き直すことが必要になります。
    この書き直す処理はメインスレッドで実行されますが、メインスレッドで無限ループの関数を呼び出していると、書き直し待ちのまま(白いまま)になります。

    この場合、重たい処理を別スレッドで実行するべきですが、VB6 ではスレッドの仕組みがないため、DLL 側で別スレッドにしてはどうかという提案が出ています。
    (そもそも、無限ループさせるなという感想を持ちますが…)

    Vista グラフィック: ATI(TM) Radeon(R) HD 2400 XT 256MB は問題ありません。
    XP   グラフィック: オンボードのビデオコントローラー(Intel Q43)     が問題です。
    OSの違いによるものでしょうか?

     Vista で起きないのは、Vista 以降では Aero (正確には Desktop Window Manager)で描画内容をキャッシュしているおかげで、再描画がこれまでの Windows に比べて減っているためです。
    (正確な表現ではないかもしれませんが、大まかな理解としては問題ないと思います)

    OS が違うから仕方がないのではなく、メインスレッドで重い処理を実行しないようにしていくべきです。
    なお、Vista では画面が白くならないかもしれませんが、タイトルバーに「応答なし」と表示されていると思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク クサキ 2010年4月28日 6:15
    2010年4月23日 15:20
    モデレータ
  • シングルスレッドのアプリケーションで、メッセージの処理を行わずに無限ループさせると画面更新などが行われず、質問のような状態になります。
    無限ループ中ならそのループの中の処理しかしていないと思ってください。
    ボタンのハンドラで無限ループさせてみればわかります。
    また、無限ループの中にDoEvents(メッセージの処理をさせる)を呼んだ場合にどうなるかを比べるとわかりやすいと思います。
    (ただしDoEventsでこの問題の対処にするのはダメです)

    スレッドは理解が難しそうに感じています。私にはまとまった時間をかけ勉強しないと分からないと
    思っていますが、もし、C++のソースの頭に何か書けくようなことで、そうなるようでしたら教えてください。
    簡単に直すのは厳しいと思います。
    スレッドを起動してそちらで処理をさせるぐらいならそうでもないのかもしれませんが(元のソースが分からないのでわかりません)、起動したスレッドは止める必要もありますし、データの受け渡しとかがあればそこをどうするかなど、考えることが増えます。
    連続的な表示で、人間の指示で終わるようなプログラムは書くにはどういう風にするのがお勧めですか?

    ・タイマで定期的に表示を更新する。
    ・画面処理(UI)と異なるスレッドで行う。
    ・ループの処理中にメッセージを処理させる。
    など、状況によって異なると思います。

    • 回答としてマーク クサキ 2010年4月28日 6:17
    2010年4月26日 7:58
  • ループの処理中にメッセージを処理させる。

    は、それでいいのですが、ループの処理中にメッセージを処理させると処理がネストされる可能性がある。ということを留意しておいてください。

    MyDoEventsのDispatchMessageでボタンなどの何かの処理が呼び出され、そこでもまた無限ループが起動され、そのなかのMyDoEventsのDispatchMessageでボタンなどの何かの処理が呼び出され、そこでもまた無限ループが起動され、そのなかのMyDoEventsのDispatchMessageでボタンなどの何かの処理が呼び出され・・・

    みたいな!

    基本的には勧めません。

    • 回答としてマーク クサキ 2010年4月28日 6:18
    2010年4月28日 0:25

すべての返信

  • コントロールの描画を行っているスレッドが、「無限ループの関数」の中に入ったまま、描画処理を行わなければコントロールの再描画は行われないためにそんな動作になっているような。

    その、「無限ループの関数」から返ってこないのであればその作りが悪いと思います。
    (常時DLL側で処理がしたいのであれば、DLL側でスレッド起動してそちらで処理をさせるとか・・・)

    • 回答としてマーク クサキ 2010年4月28日 6:15
    2010年4月23日 5:34
  • VB6.0のフォームから、Vidual Studio 2008のC++のDLLの無限ループの関数を呼び出しています。
    この時、フォームのコントロールがフォームの背景で消され見えなくなります。
    別のフォームで覆い被さったりすると起こり易いですが、まれに何にもしていなくても起こる場合があります。
    また、スクリーンセーバーに行き戻って来た時にもなります。この時はスクリーンセーバーの時の画面が残る場合もあります。(他のプログラムのフォームはチャンと描画されます)

    Windows のプログラムはそんなものです。
    ほかのウィンドウで上書きされたり、コンピュータのロックでかき消されたりした場合は、画面に書き直すことが必要になります。
    この書き直す処理はメインスレッドで実行されますが、メインスレッドで無限ループの関数を呼び出していると、書き直し待ちのまま(白いまま)になります。

    この場合、重たい処理を別スレッドで実行するべきですが、VB6 ではスレッドの仕組みがないため、DLL 側で別スレッドにしてはどうかという提案が出ています。
    (そもそも、無限ループさせるなという感想を持ちますが…)

    Vista グラフィック: ATI(TM) Radeon(R) HD 2400 XT 256MB は問題ありません。
    XP   グラフィック: オンボードのビデオコントローラー(Intel Q43)     が問題です。
    OSの違いによるものでしょうか?

     Vista で起きないのは、Vista 以降では Aero (正確には Desktop Window Manager)で描画内容をキャッシュしているおかげで、再描画がこれまでの Windows に比べて減っているためです。
    (正確な表現ではないかもしれませんが、大まかな理解としては問題ないと思います)

    OS が違うから仕方がないのではなく、メインスレッドで重い処理を実行しないようにしていくべきです。
    なお、Vista では画面が白くならないかもしれませんが、タイトルバーに「応答なし」と表示されていると思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク クサキ 2010年4月28日 6:15
    2010年4月23日 15:20
    モデレータ
  • > この場合、重たい処理を別スレッドで実行するべきですが、VB6 ではスレッドの
    > 仕組みがないため、DLL 側で別スレッドにしてはどうかという提案が出ています。

    スレッドは理解が難しそうに感じています。私にはまとまった時間をかけ勉強しないと分からないと
    思っていますが、もし、C++のソースの頭に何か書けくようなことで、そうなるようでしたら教えてください。


    >(そもそも、無限ループさせるなという感想を持ちますが…)

    連続的な表示で、人間の指示で終わるようなプログラムは書くにはどういう風にするのがお勧めですか?


    > なお、Vista では画面が白くならないかもしれませんが、タイトルバーに
    > 「応答なし」と表示されていると思います。

    VistaもXPも「応答なし」とはならず、タイトルは普通に表示されています。


    >  Vista で起きないのは、Vista 以降では Aero (正確には Desktop Window Manager)
    > で描画内容をキャッシュしているおかげで、再描画がこれまでの Windows に
    > 比べて減っているためです。

    元々、2000やXPで問題なしで動いていたものです。
    これまで、PCにはグラフィックボード(256MBなどメモリを持つ)を必ず装着していましが、
    壊れてしまったので、チェック用だけに買ったグラフィクボード無しのPCのXPで起こっています。
    グラフィクボード無しのせいかとも思いましたが、私の古いバージョンのもので動かしたら
    問題なく動いています。ということで、私のプログラムを調べてみます。

     

    2010年4月26日 7:23
  • シングルスレッドのアプリケーションで、メッセージの処理を行わずに無限ループさせると画面更新などが行われず、質問のような状態になります。
    無限ループ中ならそのループの中の処理しかしていないと思ってください。
    ボタンのハンドラで無限ループさせてみればわかります。
    また、無限ループの中にDoEvents(メッセージの処理をさせる)を呼んだ場合にどうなるかを比べるとわかりやすいと思います。
    (ただしDoEventsでこの問題の対処にするのはダメです)

    スレッドは理解が難しそうに感じています。私にはまとまった時間をかけ勉強しないと分からないと
    思っていますが、もし、C++のソースの頭に何か書けくようなことで、そうなるようでしたら教えてください。
    簡単に直すのは厳しいと思います。
    スレッドを起動してそちらで処理をさせるぐらいならそうでもないのかもしれませんが(元のソースが分からないのでわかりません)、起動したスレッドは止める必要もありますし、データの受け渡しとかがあればそこをどうするかなど、考えることが増えます。
    連続的な表示で、人間の指示で終わるようなプログラムは書くにはどういう風にするのがお勧めですか?

    ・タイマで定期的に表示を更新する。
    ・画面処理(UI)と異なるスレッドで行う。
    ・ループの処理中にメッセージを処理させる。
    など、状況によって異なると思います。

    • 回答としてマーク クサキ 2010年4月28日 6:17
    2010年4月26日 7:58
  • for loop の中で、下のような2つの関数を使い、キーボードやマウスのイベントが
    があれば、抜け出すようにたら、XPでも問題が発生しなくなりました。

    MyDoEvents();
    ret = KeyboardMouseBreak(); 
    if ( ret == ...

    今回、上手く行っていなかったのは、MyDoEvents() がなかったせいで発生していました。 

    int KeyboardMouseBreak( void )
    {
     unsigned int Status;
     int Ret = 0;
     MSG msg;
    
     HWND hWin = GetActiveWindow();
     while ( PeekMessage( &msg, hWin, WM_KEYFIRST, WM_MOUSELAST, PM_REMOVE) ) { 
       if ( msg.message == WM_RBUTTONDOWN ) { Ret = 2; return( Ret ); }
       if ( msg.message == WM_KEYDOWN ) {
      	Status = GetKeyState( VK_ESCAPE ) ;
      	if ( Status >= 0x8000 ){ Ret = BK_STATUS; return( Ret ); }
       }
       TranslateMessage( &msg );
       DispatchMessage( &msg );
     }
     return( Ret ) ;
    }
    
    void MyDoEvents( void )
    {
     MSG msg;
     while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     }
    }
    
    

    この2つの関数のことは良く分かっていないのですが、
    何とか、VB6.0のフォームからでも、C#のフォームからでも利用できるようにしたものです。
    また、MyDoEvents() と名前を付けたのはVB6.0のDoEventsのような働きをしている
    ようで、そう名づけています。(おかしいですか?)

    こんな感じのプログラムですが”ループの処理中にメッセージを処理させる。”に該当していませんか?

    2010年4月27日 9:52
  • ループの処理中にメッセージを処理させる。

    は、それでいいのですが、ループの処理中にメッセージを処理させると処理がネストされる可能性がある。ということを留意しておいてください。

    MyDoEventsのDispatchMessageでボタンなどの何かの処理が呼び出され、そこでもまた無限ループが起動され、そのなかのMyDoEventsのDispatchMessageでボタンなどの何かの処理が呼び出され、そこでもまた無限ループが起動され、そのなかのMyDoEventsのDispatchMessageでボタンなどの何かの処理が呼び出され・・・

    みたいな!

    基本的には勧めません。

    • 回答としてマーク クサキ 2010年4月28日 6:18
    2010年4月28日 0:25
  • 当面は今のままで行くしかありませんが、
    スレッドを理解できるようになりましたら書き換えたいと思います。
    色々勉強させていただきましてありがとうございました。

     

    2010年4月28日 6:26