Как я могу обновить графический интерфейс приложения win32, пока он ожидает завершения другой программы? - PullRequest
3 голосов
/ 13 февраля 2009

В настоящее время я использую CreateProcess / WaitForSingleObject из приложения win32 GUI для запуска небольшого приложения GUI, которое занимается проблемами лицензирования программного обеспечения. Это все работает нормально, но по существу зависает «родительское» приложение, пока оно ожидает завершения работы приложения лицензирования. В течение этого времени обновления родительского приложения не выполняются, и в случае перемещения окна служебного приложения оно заканчивается некрасивыми белыми квадратами.

Кроме того, по какой-то странной причине во время работы служебного приложения, если я копирую что-то из этого приложения в буфер обмена, оно ВЕСЕТ. Я еще не понял, почему, но это происходит только в том случае, если я ожидаю завершения приложения из родительского приложения.

Поэтому я думаю, что если я смогу заставить родительское приложение обрабатывать свои события в ожидании завершения работы моего другого приложения, это может решить обе проблемы.

Итак, есть ли замена для CreateProcess / WaitForSingleObject, который также обрабатывает обновления пользовательского интерфейса?

Ответы [ 5 ]

5 голосов
/ 17 февраля 2009

Ваш родительский процесс, кажется, зависает, потому что вызов WaitForSingleObject () блокирует ваш поток, пока не будет сигнализирован дескриптор, который вы передаете в вызов.

Ваш дочерний процесс, вероятно, зависает во время операции копирования в буфер обмена, поскольку он является частью этой операции, отправляя сообщение либо конкретно в окно родительского процесса, либо во все окна верхнего уровня. Цикл сообщений в потоке вашего родительского процесса не работает, поскольку он блокируется в ожидании завершения дочернего процесса, поэтому сообщение никогда не обрабатывается, а дочерний процесс остается заблокированным.

Вместо вызова WaitForSingleObject () вы можете вызвать MsgWaitForMultipleObjects () . Если вы укажете QS_ALLINPUT для параметра dwWaitMask, MsgWaitForMultipleObjects будет возвращать либо сигнал о вашем событии, либо когда есть вход в очередь сообщений потока. Если MsgWaitForMultipleObjects () возвратился из-за доступности сообщения, вы можете обработать его и возобновить ожидание:

MSG msg;
DWORD reason = WAIT_TIMEOUT;
while (WAIT_OBJECT_0 != reason) {
    reason = MsgWaitForMultipleObjects(1, &hChildProcess, FALSE, INFINITE, QS_ALLINPUT);
    switch (reason) {
    case WAIT_OBJECT_0:
        // Your child process is finished.
        break;
    case (WAIT_OBJECT_0 + 1):
        // A message is available in the message queue.
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            // Note that if your main message loop does additional processing
            // (such as calling IsDialogMessage() for modeless dialogs)
            // you will want to do those things here, too.
        }
        break;
    }
}
3 голосов
/ 13 февраля 2009

Вы можете поместить вызов WaitForSingleObject в цикл и использовать относительно небольшое значение для параметра dwMilliseconds.

Условие выхода из цикла - это когда вызов WaitForSingleObject возвращает WAIT_OBJECT_0.

В цикле вы должны изучить очередь сообщений, чтобы увидеть, есть ли какие-то, которые вы должны обработать. То, как вы справляетесь с этим, зависит от вас и зависит от ваших типичных потребностей.

<pre></p> <pre><code>// Assuming hYourWaitHandle is the handle that you're waiting on // and hwnd is your window's handle, msg is a MSG variable and // result is a DWORD variable // // Give the process 33 ms (you can use a different value here depending on // how responsive you wish your app to be) while((result = WaitForSingleObject(hYourWaitHAndle, 33)) == WAIT_TIMEOUT) { // if after 33 ms the object's handle is not signaled.. // we examine the message queue and if ther eare any waiting.. // Note: see PeekMessage documentation for details on how to limit // the types of messages to look for while(PeekMessage(&msg, hwnd, 0, 0, PM_NOREMOVE)) { // we process them.. if(GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } } } // if you're here it means WaitForSingleObject returned WAIT_OBJECT_0, so you're done // (but you should always check and make sure it really is WAIT_OBJECT_0) if(result != WAIT_OBJECT_0) { // This should not be.. so react! }

1 голос
/ 13 февраля 2009

Я предлагаю вам справиться с этим следующим образом:

  • Родительское приложение выполняет CreateProcess, а затем сразу же возвращается вместо ожидания ответа или завершения работы утилиты
  • Поскольку родительская заявка вернулась, она может обрабатывать другие сообщения Window (например, WM_PAINT)
  • Когда приложение-утилита завершает работу, оно уведомляет родительское приложение (например, с помощью API PostMessage и RegisterWindowMessage)
  • Родительское приложение обрабатывает положительные уведомления, полученные через PostMessage
  • В родительском приложении также может быть запущен таймер Windows (WM_TIMER), чтобы он знал, уничтожено ли служебное приложение, прежде чем отправлять уведомление
0 голосов
/ 13 февраля 2009

Вы должны создать поток, который выполняет только следующее:

  • вызов CreateProcess () для запуска другого приложения

  • вызовите WaitForSingleObject (), чтобы дождаться завершения процесса - поскольку это фоновый поток, ваше приложение не будет блокировать

  • вызов CloseHandle () для дескриптора процесса

  • вызов PostMessage () с уведомлением для вашей основной темы

Теперь вам нужно только убедиться, что у вашего основного приложения отключен графический интерфейс, чтобы предотвратить проблемы с повторным входом, что возможно, показывая модальное диалоговое окно, информирующее пользователя о том, что другое приложение запущено и с которым нужно разобраться в первую очередь. Убедитесь, что диалоговое окно не может быть закрыто вручную, и что оно закрывается, когда получает отправленное уведомление из фоновой цепочки. Вы также можете поместить весь процесс создания потока в этот диалог и обернуть все в одну функцию, которая создает, отображает и уничтожает диалог и возвращает результат, полученный вашим внешним приложением.

0 голосов
/ 13 февраля 2009

Вы можете столкнуться с проблемой зависания, если приложение, которое вы порождаете, вызывает передачу sendmessage, явную или неявную. Это вырезано с моего сайта:

Проблема возникает из-за того, что у вашего приложения есть окно, но оно не перекачивает сообщения. Если порожденное приложение вызывает SendMessage с одной из целей широковещания (HWND_BROADCAST или HWND_TOPMOST), то SendMessage не вернется в новое приложение, пока все приложения не обработают сообщение - но ваше приложение не может обработать сообщение, потому что оно не ' закачивание сообщений .... так что новое приложение блокируется, так что ваше ожидание никогда не удается .... DEADLOCK.

Я не делаю код буфера обмена, но если это вызывает ситуацию выше (правдоподобно), то вы зашли в тупик. Вы можете:

  • поместите запуск вторичного приложения в небольшой поток
  • использовать тайм-аут и вращаться вокруг цикла PeekMessage (гадость)
  • использовать API MsgWaitForMultipleObjects.

Этот порядок не подразумевает никаких предпочтений ... Я предполагаю, что вы сами не создаете порожденное приложение, и в этом случае вы можете использовать IPC, чтобы обойти эту проблему, как предложил ChrisW.

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