SetWindowText関数についてOSの違いによる影響

解決


名古屋  2005-02-16 13:39:48  No: 56436  IP: 192.*.*.*

お世話になります。
VC++6.0について質問です。

処理時間の長いプログラムで、処理状況を表示し知らせるため、
スタティックテキストに、処理している内容をSetWindowText関数
にて逐次表示しています。

Windows2000では、正常に表示されるのですが、
WindowsXPでは、表示更新が途中で止まってしまうことがあります。
処理本体は問題なく終了しますが、ユーザが見た場合、処理が
とまって見えてしまい強制終了されてしまう恐れがあるため
何とか対策をとりたいと思っています。

よろしくお願いします。

編集 削除
たく  2005-02-16 14:28:46  No: 56437  IP: 192.*.*.*

適当にメッセージポンプをはさんでもだめですか?

編集 削除
名古屋  2005-02-16 15:16:56  No: 56438  IP: 192.*.*.*

発言ありがとうございます。

>適当にメッセージポンプをはさんでもだめですか?

「メッセージポンプ」とは何でしょうか?
ネットで調べてみましたがなかなか難しいことが書いてあり
理解できないでいます。

簡単に説明していただきたいのですが?
(メッセージポンプについて説明のあるURLでも構いません。)

宜しくお願いします。

ちなみに、その方法の場合、簡単に修正できるのでしょうか?

編集 削除
てつや  2005-02-16 15:21:06  No: 56439  IP: 192.*.*.*

MSDN <アイドル ループ処理 > より

while ( bDoingBackgroundProcessing ) 

    MSG msg;
    while ( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) 
    { 
        if ( !PumpMessage( ) ) 
        { 
            bDoingBackgroundProcessing = FALSE; 
            ::PostQuitMessage( ); 
            break; 
        } 
    } 
    // let MFC do its idle processing
    LONG lIdle = 0;
    while ( AfxGetApp()->OnIdle(lIdle++ ) )
        ;  
    // Perform some background processing here 
    // using another call to OnIdle
}
長い処理にはさんでおくと、メッセージ処理がされます。
ループがあまりにも長い場合は役に立ちませんが。

編集 削除
名古屋  2005-02-17 09:21:36  No: 56440  IP: 192.*.*.*

発言ありがとうございます。

メッセージ出力処理の後に、ご指摘のコードを挿入
してみました。
「if ( !PumpMessage( ) )」の部分でメッセージ
がすべてなくなれば、ループを出るというような
流れだと思うのですが、この条件がいっこうに
TRUEにならないため、永久ループに陥ってしまいました。

ご指摘のコードに何か変更を加える必要はあるのでしょうか?

編集 削除
てつや  2005-02-17 11:13:37  No: 56441  IP: 192.*.*.*

あ、アイドル処理だから何もしていないんですね。
安易にコピペしてしまいました。すみません。
メッセージループとはSDKスケルトンプロジェクトを作った時に記述されている、
   // メイン メッセージ ループ:
   while( GetMessage(&msg, NULL, 0, 0) ) 
   {
      if( !TranslateAccelerator (msg.hwnd, hAccelTable, &msg) ) 
      {
         TranslateMessage( &msg );
         DispatchMessage( &msg );
      }
   }
この部分です。プログラムが終了するまで、ひたすらメッセージを受け取りつづけます。
で、ループの中で似たようなことをやるわけですが、ひたすら受け取りつづけたら、先に進まないので、自分である程度の見切りをつけます。
   int i = 0 ;
   while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
   {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
      // 条件を入れて、抜け出す術をつくる。
      if( i == 10 )
         break ;
      i++ ;
   }
とか
   // 1ループにつき1このメッセージしか処理しない。
   if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
   {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
当然ループ内の処理は遅くなりますし、ループが長い場合はここにたどり着く迄
無応答になります。

編集 削除
名古屋  2005-02-18 13:06:26  No: 56442  IP: 192.*.*.*

発言ありがとうございます。

今XPの環境が無いため月曜日に確認してみます。

編集 削除
名古屋  2005-02-23 15:09:05  No: 56443  IP: 192.*.*.*

ご指摘の通り、10回ループしたら強制的にループを
抜けるように変更しました。

結果永久ループに陥ることもなく、
メッセージ出力処理も行われるようになりました。

大変助かりました。
ありがとうございます。

編集 削除
Gak  2005-02-24 14:15:11  No: 56444  IP: 192.*.*.*

解決チェックが付いた後 && 憶測 でアレなんだけど…

簡単に

  SetWindowText(hWnd, "...");
  UpdateWindow(hWnd);

でいけそうな気がしないでもない。

編集 削除
名古屋  2005-02-24 16:59:29  No: 56445  IP: 192.*.*.*

発言ありがとうございます。

VC++の経験が浅いためよくわからないのですが、
引数が1つ増えてウィンドウハンドルを渡しているようですが、
引数が1つの場合と2つの場合で何か違いがあるのでしょうか?

ちなみに、CWndクラスの「SetWindowText」は引数が1つのようですが?

編集 削除
Gak  2005-02-24 18:10:42  No: 56446  IP: 192.*.*.*

MFC でしたか。ならば

  // 「この文字列を表示してくれ」と命令
  スタティックテキスト.SetWindowText("...");

のような感じでスタティックテキストに文字列を設定していると思います。
これを

  // 「この文字列を表示してくれ」と命令
  スタティックテキスト.SetWindowText("...");
  // 「今すぐ最優先で。他の仕事は後回しにしてでも表示しろ」と命令
  スタティックテキスト.UpdateWindow();

としたらどうだろうって事です。

> ちなみに、CWndクラスの「SetWindowText」は引数が1つのようですが?
私の前述の書き方については「MFC」「SDK」等でぐぐってみれば情報が得られるかと。

編集 削除
名古屋  2005-02-25 09:52:17  No: 56447  IP: 192.*.*.*

発言ありがとうございます。

>  SetWindowText("...");
>  UpdateWindow();

ご指摘の上記コードを試してみました。
XPでは、「UpdateWindow()」もメッセージキュー
に溜まってしまい、アプリケーションの処理の
方が優先されてしまいました。
(結果メッセージ更新されませんでした。)

XPでは、アプリケーションの処理中に
メッセージキューに溜まったメッセージは
強制的に出してあげないといけないのかもしれません。

編集 削除
Gak  2005-02-25 14:25:07  No: 56448  IP: 192.*.*.*

> (結果メッセージ更新されませんでした。)
ダメでしたか…

  SetWindowText("...");
  Invalidate(FALSE);
  UpdateWindow();

では上記のようにしてみたらどうでしょう?
コレでダメなら私の述べた事は所詮憶測だったという事で許してください。

# メンド臭かったら放置してくれて良いです。解決済みの件でもありますし。

編集 削除
たく  2005-02-25 15:14:34  No: 56449  IP: 192.*.*.*

だめです。

そもそも
InvalidateやUpdateWindowから投げられる
WM_PAINTやWM_ERASEBKGNDがキューにたまっているという話です。

編集 削除
たく  2005-02-25 15:20:09  No: 56450  IP: 192.*.*.*

↑には微妙に嘘があるので忘れてください^^;

編集 削除
てつや  2005-02-26 00:04:47  No: 56451  IP: 192.*.*.*

>  SetWindowText("...");
>  UpdateWindow();
"う、その手があったか。"って思ってました。
(ただ、タスクマネージャでは"応答なし"になってしまうとは思いますが。)

>ご指摘の上記コードを試してみました。
>XPでは、「UpdateWindow()」もメッセージキュー
>に溜まってしまい、アプリケーションの処理の
>方が優先されてしまいました。

MSDN <CWnd::UpdateWindow>の説明
UpdateWindow メンバ関数は、アプリケーションのキューをバイパスして、直接 WM_PAINT メッセージを送ります。更新領域が空の場合、WM_PAINT メッセージは送信されません。

UpdateWindowはWM_PAINTをSendMessageしているはずです。
XPだけ違うふるまいをするとは思えません。
MFCのUpdateWindowはクラス毎にもってますから、対象を間違えている
可能性はありませんか?
(違ってたらごめんなさい)

編集 削除
Gak  2005-02-26 14:09:46  No: 56452  IP: 192.*.*.*

> InvalidateやUpdateWindowから投げられる
> WM_PAINTやWM_ERASEBKGNDがキューにたまっているという話です。
既に嘘と書かれてますが、一応意図説明。

Invalidate() を入れたのは WM_PAINT を投げて貰うのが目的ではなく
更新領域を確実に追加するため、念のための処理です。
UpdateWindow() についてはてつやさんのレスに書かれているように
WM_PAINT を直接処理させるためです。

まあ、意図通り動くかどうかは?ですが。

編集 削除
たく  2005-03-01 10:30:27  No: 56453  IP: 192.*.*.*

しつこいですが^^;
だめです

微妙な嘘というのは、
SendMessageとPostMessageの違いのことです。
(WM_PAINT自体はキューにたまっていない)

ですが、根本的に他のメッセージが滞留しているために起こってる現象ですので、
キュー上の滞留メッセージを処理する必要があります。

編集 削除
Gak  2005-03-01 11:52:48  No: 56454  IP: 192.*.*.*

> ですが、根本的に他のメッセージが滞留しているために起こってる現象ですので、
> キュー上の滞留メッセージを処理する必要があります。
確かにキュー上に滞留メッセージが存在するかぎり WM_PAINT は処理する機会を与えられません。

でも、キュー上にメッセージが滞留している状態でも WM_PAINT を処理したい時に使用するような
関数が UpdateWindow() だと認識していたのですが…違うんでしょうか?

> だめです
何で「だめ」なのかまだ解りません。良かったら付き合ってください。

編集 削除
たく  2005-03-01 16:22:15  No: 56455  IP: 192.*.*.*

consoleプログラムでなく、Windowプログラムで
(描画は更新されているとはいえ)無応答はまずいよね

編集 削除
Gak  2005-03-01 17:17:53  No: 56456  IP: 192.*.*.*

> consoleプログラムでなく、Windowプログラムで
> (描画は更新されているとはいえ)無応答はまずいよね
そういう意味での「ダメ」ですか…了解です。

「ダメ」==「キューにメッセージが溜まっていると UpdateWindow() でも描画の更新はできない」
と言っている、と捉えてました。

編集 削除