【问题标题】:Terminate Qt Process: What's Windows Task Manager doing that I'm not?终止 Qt 进程:Windows 任务管理器在做什么而我不是?
【发布时间】:2014-12-30 10:26:29
【问题描述】:

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

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

我正在使用 Microsoft 概述的 this method。甚至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;
}

【问题讨论】:

  • QT 通常知道什么时候需要退出,并且会干净利落地退出,除非开发人员搞砸了,您可以通过查看(不可用的)源代码来检查...
  • @SirDarius:这也是我的思考过程,但是在这个程序管理的所有应用程序中,只有 Qt 有这个问题(总共 3 个),而且它们都是由不同的作者编写的。巧合?此外,任务管理器可以干净地关闭它们,但是如何..?
  • 我可能会建议您尝试停止第三方开源 qt 程序,例如您可以调试的 cmake gui 并查看它是如何实现的
  • 也许 Qt (不正确地)期望 wParam 和/或 lParam 具有某些特定值?
  • @SirDarius:我刚刚尝试了 CMake gui。它有同样的问题 - 窗口关闭但进程仍然存在

标签: c++ windows qt winapi kill-process


【解决方案1】:

好的,解决了。

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

有趣的是,通过使用任务管理器,QEventDispatcher 不会获得 WM_CLOSE 消息,因此保持活动状态,因此当主窗口收到 WM_CLOSE 消息时,进程干净地退出。我只能假设在任务管理器的EnumWindowsProc callback 中使用了对IsWindowVisible 之类的调用,这与他们的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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多