2014-11-03 3 views
5

У меня есть приложение, задачей которого является запуск и остановка различных других процессов.Завершение процесса Qt: что делает диспетчер задач Windows, что я не?

Проблема в том, что приложения Qt не прекращаются чисто. Окно Qt закрывается, но процесс все еще работает в фоновом режиме, пока не вызывается TerminateProcess(), а затем приложение Qt выходит без очистки.

Я использую this method, как указано Microsoft. Даже Qt source использует этот метод для завершения процессов, за исключением того, что они также отправляют WM_CLOSE в основной поток. Я добавил это к моему приложению, но он все еще закрывает окно, оставляя этот процесс.

Интересно, что если я использую диспетчер задач Windows для «Завершить задачу» («Не завершать процесс»), окно закрывается, и процесс также заканчивается, поэтому я знаю, что это возможно. Если я использую spy ++, я вижу, что основное окно и основной поток принимают сообщения WM_CLOSE из диспетчера задач и моего приложения, но только с помощью диспетчера задач сообщения продолжаются до WM_DESTROY, WM_NCDESTROY и т. Д. И заканчиваются завершением процесса , Эта проблема возникает только с приложениями Qt. Приложения Win32/MFC и т. Д. Прекращают использование моего приложения.

Как вы должны полностью закрыть приложения Qt (предположим, что источник приложения Qt недоступен)?

-------- Редактировать --------

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

Образец кода начинается с CMake (download here), но любое приложение Qt должно делать.

#include <Windows.h> 
#include <iostream> 

BOOL CALLBACK TerminateAppEnum(HWND hwnd, LPARAM pid); 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    char* processName = "C:\\Program Files (x86)\\CMake\\bin\\cmake-gui.exe"; 
    //char* processName = "C:\\Windows\\Notepad.exe"; 

    std::cout << "Creating process \"" << processName << "\"" << std::endl; 

    STARTUPINFO si = {0}; 
    si.cb = sizeof(STARTUPINFO); 
    PROCESS_INFORMATION pi = {0}; 

    BOOL success = CreateProcess(processName, 
           "", 
           NULL, 
           NULL, 
           FALSE, 
           0, 
           NULL, 
           NULL, 
           &si, 
           &pi); 

    if (success) 
    { 
    std::cout << "Press any key to cleanly terminate process..." << std::endl; 
    std::cin.get(); 

    std::cout << "Cleanly terminating process..." << std::endl; 

    EnumWindows(TerminateAppEnum, (LPARAM)pi.dwProcessId); 
    PostThreadMessage(pi.dwThreadId, WM_CLOSE, 0, 0); 

    if (WaitForSingleObject(pi.hProcess, 10000) == WAIT_OBJECT_0) 
    { 
     std::cout << "Success! The process has terminated" << std::endl; 
    } 
    else 
    { 
     std::cout << "Failed! The process is still running" << std::endl; 
    } 
    CloseHandle(pi.hProcess); 
    CloseHandle(pi.hThread); 
    } 
    else 
    { 
    std::cout << "Unable to start process (Error " << GetLastError() << ")" << std::endl; 
    } 
    std::cout << "Press any key to exit..." << std::endl; 
    std::cin.get(); 

    return 0; 
} 

BOOL CALLBACK TerminateAppEnum(HWND hwnd, LPARAM pid) 
{ 
    DWORD dwPID; 
    GetWindowThreadProcessId(hwnd, &dwPID); 

    if (dwPID == (DWORD)pid) 
    { 
    PostMessage(hwnd, WM_CLOSE, 0, 0); 
    } 

    return TRUE; 
} 
+2

QT обычно знает, когда ему нужно выйти, и делает это чисто, за исключением того, если разработчик перепутались, который вы могли бы проверить, посмотрев на (нет) исходный код ... – SirDarius

+0

@SirDarius: Это тоже мой мыслительный процесс, но из всех приложений, которыми управляет эта программа, только у Qt есть эта проблема (всего 3), и все они принадлежат другим авторам. Совпадение? Кроме того, диспетчер задач может их закрыть, но как ..? – parrowdice

+0

Я могу предложить вам попробовать остановить сторонние программы QT с открытым исходным кодом, такие как cmake gui, которые вы могли бы отлаживать, и посмотреть, как он был реализован. – SirDarius

ответ

2

Хорошо, решил.

Проблема возникает из-за того, что Qt создает окно верхнего уровня - окно QEventDispatcher. Следуя процедуре, описанной Microsoft, это окно получает сообщение WM_CLOSE, которое закрывает это окно и его поток. После этого, когда основное окно приложения закрыто, очистка не производится, и процесс остается в системной памяти.

Что интересно, с помощью диспетчера задач, то QEventDispatcher не получить сообщение WM_CLOSE так остается активным, и поэтому, когда главное окно Ges сообщение WM_CLOSE процесс выходит чисто. Я могу только предположить, что вызов IsWindowVisible используется в EnumWindowsProc callback в диспетчере задач, в отличие от их documentation. Хотя эта документация была в последний раз рассмотрена более десяти лет назад!

Добавление в призыв к IsWindowVisible делает работу программы со всеми приложениями Qt, а другие приложения, отличные от Qt, похоже, продолжают работать и с этим изменением. Для полноты картины, я включил обновленный пример кода:

#include <Windows.h> 
#include <iostream> 

BOOL CALLBACK TerminateAppEnum(HWND hwnd, LPARAM pid); 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    char* processName = "C:\\Program Files (x86)\\CMake\\bin\\cmake-gui.exe"; 
    //char* processName = "C:\\Windows\\Notepad.exe"; 

    std::cout << "Creating process \"" << processName << "\"" << std::endl; 

    STARTUPINFO si = {0}; 
    si.cb = sizeof(STARTUPINFO); 
    PROCESS_INFORMATION pi = {0}; 

    BOOL success = CreateProcess(processName, 
           "", 
           NULL, 
           NULL, 
           FALSE, 
           0, 
           NULL, 
           NULL, 
           &si, 
           &pi); 

    if (success) 
    { 
    std::cout << "Press any key to cleanly terminate process..." << std::endl; 
    std::cin.get(); 

    std::cout << "Cleanly terminating process..." << std::endl; 

    EnumWindows(TerminateAppEnum, (LPARAM)pi.dwProcessId); 

    if (WaitForSingleObject(pi.hProcess, 10000) == WAIT_OBJECT_0) 
    { 
     std::cout << "Success! The process has terminated" << std::endl; 
    } 
    else 
    { 
     std::cout << "Failed! The process is still running" << std::endl; 
    } 
    CloseHandle(pi.hProcess); 
    CloseHandle(pi.hThread); 
    } 
    else 
    { 
    std::cout << "Unable to start process (Error " << GetLastError() << ")" << std::endl; 
    } 
    std::cout << "Press any key to exit..." << std::endl; 
    std::cin.get(); 

    return 0; 
} 

BOOL CALLBACK TerminateAppEnum(HWND hwnd, LPARAM pid) 
{ 
    DWORD dwPID; 
    GetWindowThreadProcessId(hwnd, &dwPID); 

    if (dwPID == (DWORD)pid) 
    { 
    if (IsWindowVisible(hwnd)) 
    { 
     PostMessage(hwnd, WM_CLOSE, 0, 0); 
    } 
    } 

    return TRUE; 
}