Мне нужен насос сообщений, который не испортит мое открытое окно - PullRequest
0 голосов
/ 06 мая 2009

Мое приложение (загрузочное приложение для установщика, над которым я работаю, должно запустить некоторые другие приложения (мой установщик и сторонние установщики для предварительных требований моего установщика) и дождаться их завершения. Для того, чтобы графический интерфейс мог обновляя экран во время ожидания завершения приложения, я поместил насос сообщений в цикл ожидания, используя пример «MFC-совместимый» в документации Visual Studio по обработке цикла ожидания в качестве руководства. Мой код (который находится в функции-члене) класса, производного от CWinApp), выглядит следующим образом:

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 ()), так что я не знаю, что здесь происходит, чтобы вызвать дескрипторы окна и меню. юг. Если у меня нет сообщения, все работает нормально, за исключением того, что графический интерфейс не может обновляться, пока работает цикл ожидания.

У кого-нибудь есть идеи, как заставить это работать? В качестве альтернативы я хотел бы запустить рабочий поток для запуска второго приложения, если bWait имеет значение ИСТИНА, но я никогда раньше ничего не делал с потоками, поэтому мне понадобится несколько советов о том, как это сделать без проблем с синхронизацией и т. Д . (Примеры кода были бы очень полезны в любом случае.)

Ответы [ 4 ]

2 голосов
/ 07 мая 2009

Я также разместил этот вопрос на форумах Microsoft, и благодаря помощи одного из них Дуга Харриса в Microsoft я обнаружил, что моя проблема с моими значениями HWND и HMENU, действительно из-за устаревших указателей CWwnd * и CMenu * (получено с помощью вызовов GetMenu () и GetDialogItem (). Повторное получение указателей после запуска второго приложения решило эту проблему. Кроме того, он указал мне на веб-сайт *, на котором показан лучший способ выполнения цикла с использованием MsgWaitForMultipleObjects () для управления это не включает в себя занятую работу ожидания определенного количества времени и опроса процесса для кода выхода.

Мой цикл теперь выглядит так:

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);

* Этот веб-сайт, если вам интересно, это: http://members.cox.net/doug_web/threads.htm

1 голос
/ 07 мая 2009

Я думаю, что ваша проблема в WaitForSingleObject

Глядя в MSDN Вы видите это

Соблюдайте осторожность при вызове функций ожидания и кода, который прямо или косвенно создает окна. Если поток создает какие-либо окна, он должен обрабатывать сообщения. Сообщения трансляций отправляются во все окна системы. Поток, использующий функцию ожидания без интервала времени ожидания, может привести к зависанию системы. Два примера кода, который косвенно создает окна, - это DDE и функция CoInitialize. Поэтому, если у вас есть поток, который создает окна, используйте MsgWaitForMultipleObjects или MsgWaitForMultipleObjectsEx, а не WaitForSingleObject.

В моем коде в насосе сообщений используйте MsgWaitForMultipleObjects ( doc ).

При звонке этот звонок.

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

Это должно остановить вашу проблему с исчезновением ресурсов.

0 голосов
/ 08 декабря 2011

Существует функция Windows, которая называется DisableProcessWindowsGhosting (см. http://msdn.microsoft.com/en-us/library/ms648415(v=vs.85).aspx)), которая не позволяет Windows «скрывать» ваше окно и продолжает обновлять окно (анимацию).

0 голосов
/ 07 мая 2009

Когда вы говорите, что дескрипторы окна и меню, кажется, освобождены, вы имеете в виду, что у вас есть фактические значения HWND и HMENU, которые больше не работают, или у вас есть переменные MFC CWnd * и CMenu *, которые не работают

Если последнее, проблема, скорее всего, заключается в том, что вы получаете указатели CWnd *, вызывая CWnd :: FromHandle () (или CMenu :: FromHandle ()) где-то (или вызывая что-то, что вызывает их), и OnIdle () отбрасывает их.

Основная причина заключается в том, что MFC поддерживает карту из дескрипторов окна (или меню и т. Д.) Для объектов CWnd * в системе. Когда вызывается CWnd :: FromHandle (), он ищет совпадение на карте: если оно найдено, оно возвращается. Если нет, то создается новый временный CWnd, добавляется на карту и возвращается. Идея OnIdle () заключается в том, что когда он вызывается, вся обработка сообщений выполняется, поэтому OnIdle () отбрасывает любой из этих временных объектов CWnd, которые все еще существуют. Вот почему документация CWnd :: FromHandle () предупреждает, что возвращаемый указатель может быть временным.

«Правильное» решение этой проблемы - не висеть на указателях CWnd *, возвращаемых из CWnd :: FromHandle (). Учитывая простоту вашего приложения, может быть проще просто удалить вызов OnIdle (): это не должно иметь негативных последствий для установщика.

Конечно, это всего лишь предположение, но это звучит правдоподобно ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...