0

私のアプリケーション (私が取り組んでいるインストーラーのブートストラップ アプリケーションは、いくつかの他のアプリケーション (私のインストーラーと私のインストーラーの前提条件のためのサード パーティのインストーラー) を起動し、それらが完了するのを待つ必要があります。GUI が画面の更新を行えるようにするためにアプリが完了するのを待っている間、アイドル ループ処理に関する Visual Studio ドキュメントの「MFC 互換」の例をガイドラインとして使用して、待機ループにメッセージ ポンプを配置しました。 -派生クラス) は次のとおりです。

if (::CreateProcess(lpAppName, szCmdLineBuffer, NULL, NULL, TRUE, 0, NULL, NULL,
                    &StartupInfo, &ProcessInfo))
{
  ::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
  if (bWait)
    while (dwExitCode == STILL_ACTIVE)
    {
      // In order to allow updates of the GUI to happen while we're waiting for
      // the application to finish, we must run a mini message pump here to
      // allow messages to go through and get processed.  This message pump
      // performs much like MFC's main message pump found in CWinThread::Run().
      MSG msg;
      while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
      {
        if (!PumpMessage())
        {
          // a termination message (e.g. WM_DESTROY)
          // was processed, so we need to stop waiting
          dwExitCode = ERROR_CANT_WAIT;
          ::PostQuitMessage(0);
          break;
        }
      }

      // let MFC do its idle processing
      LONG nIdle = 0;
      while (OnIdle(nIdle++))
        ;

      if (dwExitCode == STILL_ACTIVE) // was a termination message processed?
      {
        // no; wait for .1 second to see if the application is finished
        ::WaitForSingleObject(ProcessInfo.hProcess, 100);
        ::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
      }
    }
  ::CloseHandle(ProcessInfo.hProcess);
  ::CloseHandle(ProcessInfo.hThread);
}
else
  dwExitCode = ::GetLastError();

私が抱えている問題は、ある時点で、このメッセージ ポンプが、このコードの実行時に開いていたウィンドウとウィンドウのメニュー ハンドルを解放しているように見えることです。私はデバッガーでウォークスルーを行いましたが、if (!PumpMessage()) ステートメントの本体に入ることは一度もなかったので、ここで何が起こってウィンドウとメニューハンドルが移動するのかわかりません南。メッセージ ポンプがない場合、待機ループの実行中に GUI が更新できないことを除いて、すべて正常に動作します。

これを機能させる方法について誰かアイデアがありますか? または、bWait が TRUE の場合にワーカー スレッドを起動して 2 番目のアプリを起動したいのですが、これまでスレッドで何もしたことがないので、同期の問題を発生させずに実行する方法についてアドバイスが必要です。 (どちらの場合も、コード例は大歓迎です。)

4

4 に答える 4

2

Microsoft フォーラムにもこの質問を投稿しました。Microsoft の Doug Harris の助けのおかげで、HWND と HMENU の値に関する問題が、古い CWwnd* と CMenu* ポインター ( GetMenu() と GetDialogItem() 呼び出し. 2 番目のアプリを起動した後に再度ポインターを取得すると、その問題は解決しました. また、彼は、MsgWaitForMultipleObjects() を使用してループを制御するより良い方法を示した Web サイト* を教えてくれました。一定時間待機し、プロセスをポーリングして終了コードを取得するという忙しい作業は必要ありません。

私のループは次のようになります。

if (bWait)
{
  // In order to allow updates of the GUI to happen while we're
  // waiting for the application to finish, we must run a message
  // pump here to allow messages to go through and get processed.
  LONG  nIdleCount = 0;
  for (;;)
  {
    MSG msg;
    if (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
      PumpMessage();
    else //if (!OnIdle(nIdleCount++))
    {
      nIdleCount = 0;
      if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
      {
        DWORD nRes = ::MsgWaitForMultipleObjects(1, &ProcessInfo.hProcess,
                                                 FALSE, INFINITE, QS_ALLEVENTS);
        if (nRes == WAIT_OBJECT_0)
          break;
      }
    }
  }
}
::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);

*興味のある方は、その Web サイトをご覧ください: http://members.cox.net/doug_web/threads.htm

于 2009-05-07T16:42:23.227 に答える
1

あなたの問題はWaitForSingleObject

MSDNを見ると、これが表示されます

ウィンドウを直接的または間接的に作成する待機関数およびコードを呼び出す場合は注意してください。スレッドがウィンドウを作成する場合、メッセージを処理する必要があります。メッセージ ブロードキャストは、システム内のすべてのウィンドウに送信されます。タイムアウト間隔のない待機関数を使用するスレッドにより、システムがデッドロック状態になる可能性があります。ウィンドウを間接的に作成するコードの 2 つの例は、DDE と CoInitialize 関数です。したがって、ウィンドウを作成するスレッドがある場合は、WaitForSingleObject ではなく、MsgWaitForMultipleObjects または MsgWaitForMultipleObjectsEx を使用してください。

メッセージ ポンプの私のコードでは、use MsgWaitForMultipleObjects( doc ) を使用します。

コールでこのコール。

MsgWaitForMultipleObjects(1, &ProcessInfo.hProcess, FALSE, 100, QS_ALLEVENTS); 

これにより、リソースが消えるという問題が解決するはずです。

于 2009-05-07T00:35:55.717 に答える
0

DisableProcessWindowsGhosting ( http://msdn.microsoft.com/en-us/library/ms648415(v=vs.85).aspxを参照)と呼ばれる Windows 機能があり、Windows がウィンドウを「ゴースト化」するのを防ぎ、更新を続行します。ウィンドウ(あなたのアニメーション)。

于 2011-12-08T08:32:08.413 に答える
0

ウィンドウとメニューのハンドルが解放されているように見えると言うとき、実際の HWND と HMENU の値が機能しなくなったということですか、それとも MFC の CWnd* と CMenu* 変数が失敗したということですか?

後者の場合、どこかで CWnd::FromHandle() (または CMenu::FromHandle()) を呼び出して (またはそれらを呼び出す何かを呼び出して) CWnd* ポインターを取得している可能性が高く、OnIdle() はそれらを破棄します。

根本的な理由は、MFC がウィンドウ (またはメニューなど) ハンドルからシステム内の CWnd* オブジェクトへのマップを保持しているためです。CWnd::FromHandle() が呼び出されると、マップ内で一致するものが検索されます。見つかった場合は、それが返されます。そうでない場合は、新しい一時的な CWnd が作成され、マップに追加されて返されます。OnIdle() の背後にある考え方は、呼び出されるとすべてのメッセージ処理が完了するため、OnIdle() はまだ存在するこれらの一時的な CWnd オブジェクトをすべて破棄するというものです。そのため、CWnd::FromHandle() のドキュメントでは、返されたポインターが一時的なものである可能性があると警告されています。

これに対する「正しい」解決策は、CWnd::FromHandle() から返された CWnd* ポインターに依存しないことです。アプリケーションの単純さを考えると、呼び出し OnIdle() を削除する方が簡単かもしれません: これはインストーラーに悪影響を与えるべきではありません。

もちろん、これはすべて推測ですが、もっともらしく聞こえます...

于 2009-05-07T08:08:03.290 に答える