У меня есть приложение, работа которого заключается в запуске и остановке различных других процессов.
Проблема в том, что приложения Qt не останавливаются чисто. Окно Qt закрывается, но процесс по-прежнему выполняется в фоновом режиме до тех пор, пока не будет вызван TerminateProcess (), а затем приложение Qt завершит работу без очистки.
я использую Этот метод как указано Microsoft. Даже Qt source использует этот метод для завершения процессов, за исключением того, что они также публикуют WM_CLOSE в основной поток. Я добавил это и в свое приложение, но оно по-прежнему просто закрывает окно, оставляя процесс.
Что мне кажется интересным, так это то, что если я использую диспетчер задач Windows для «Завершения задачи» (не «Завершить процесс»), окно закрывается, и процесс тоже завершается, поэтому я знаю, что это возможно. Если я использую spy ++, я вижу, что главное окно и основной поток оба получают сообщения WM_CLOSE как из диспетчера задач, так и из моего приложения, но только с помощью диспетчера задач эти сообщения переходят к WM_DESTROY, WM_NCDESTROY и т. Д. И заканчиваются завершением процесса. , Эта проблема возникает только с приложениями Qt. Win32 / MFC и т. Д. Приложения завершаются чисто с помощью моего приложения.
Как вы должны аккуратно закрывать приложения Qt (если исходный код приложения Qt недоступен)?
———Редактировать———
Вот пример кода, который воспроизведет проблему. По крайней мере, мне было бы интересно узнать, видят ли другие люди ту же проблему, что и я.
Пример кода запускает CMake (Скачать здесь), но подойдет любое приложение 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;
}
Хорошо, решил это.
Проблема заключается в том, что Qt создает окно верхнего уровня — QEventDispatcher
окно. В соответствии с процедурой, описанной Microsoft, это окно получает сообщение WM_CLOSE, которое закрывает это окно и его поток. После этого, когда главное окно приложения закрывается, очистка не выполняется, и процесс остается в системной памяти.
Что интересно, с помощью диспетчера задач QEventDispatcher не получить сообщение WM_CLOSE, чтобы оно оставалось активным, и поэтому, когда в главном окне появляется сообщение WM_CLOSE, процесс завершается чисто. Я могу только предположить, что вызов что-то вроде IsWindowVisible используется в Обратный вызов EnumWindowsProc в диспетчере задач, вопреки их документация. Хотя эта документация была в последний раз рассмотрена более десяти лет назад!
Добавление вызова 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;
}