终止Qt过程:Windows任务管理器在做什么我不是?

par*_*ice 5 c++ windows winapi qt kill-process

我有一个应用程序,其工作是启动和停止各种其他进程.

问题是Qt应用程序不能完全停止.Qt窗口关闭,但进程仍然在后台运行,直到调用TerminateProcess(),然后Qt应用程序退出而不进行清理.

我正在使用Microsoft概述的这种方法.甚至Qt源也使用该方法来终止进程,除了它们还将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;
}
Run Code Online (Sandbox Code Playgroud)

par*_*ice 2

好吧,解决了。

问题来自于 Qt 创建了一个顶级窗口——一个QEventDispatcher窗口。按照 Microsoft 概述的过程,此窗口将收到 WM_CLOSE 消息,该消息将关闭该窗口及其线程。此后,当应用程序的主窗口关闭时,不会进行任何清理,进程仍保留在系统内存中。

有趣的是,通过使用任务管理器,QEventDispatcher不会收到 WM_CLOSE 消息,因此保持活动状态,因此当主窗口收到 WM_CLOSE 消息时,进程会干净地退出。我只能假设在任务管理器的EnumWindowsProc 回调中使用了对IsWindowVisible之类的调用,这与他们的文档相反。尽管该文档的最后一次审查是在十多年前!

添加对 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;
}
Run Code Online (Sandbox Code Playgroud)