简体   繁体   English

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

[英]Terminate Qt Process: What's Windows Task Manager doing that I'm not?

I have an application whose job is to start and stop various other processes. 我有一个应用程序,其工作是启动和停止各种其他进程。

The problem is that Qt applications don't stop cleanly. 问题是Qt应用程序不能完全停止。 The Qt window closes, but the process still runs in the background, until TerminateProcess() is called and then the Qt app exits without cleaning up. Qt窗口关闭,但进程仍在后台运行,直到调用TerminateProcess(),然后Qt应用程序退出而不进行清理。

I'm using this method as outlined by Microsoft. 我正在使用Microsoft概述的这种方法 Even the Qt source uses that method for terminating processes, except they also post a WM_CLOSE to the main thread. 甚至Qt源也使用该方法来终止进程,除了它们还将WM_CLOSE发布到主线程。 I've added that in to my application too but it still just closes the Window, leaving the process. 我已将其添加到我的应用程序中,但它仍然只是关闭窗口,离开了该过程。

What I find interesting is that if I use Windows Task Manager to "End Task" (Not "End Process"), the window closes and the process ends too, so I know it's possible. 我觉得有趣的是,如果我使用Windows任务管理器来“结束任务”(不是“结束进程”),窗口关闭,进程也结束,所以我知道这是可能的。 If I use spy++ I can see that the main window and the main thread both receive WM_CLOSE messages from both Task Manager and my application, but only by using Task Manager do the messages continue on to WM_DESTROY, WM_NCDESTROY etc and end up with the process ending. 如果我使用spy ++我可以看到主窗口和主线程都从任务管理器和我的应用程序接收WM_CLOSE消息,但只有使用任务管理器,消息继续到WM_DESTROY,WM_NCDESTROY等,最后结束进程结束。 This problem only happens with Qt applications. 这个问题只发生在Qt应用程序中。 Win32 / MFC etc applications terminate cleanly using my application. Win32 / MFC等应用程序使用我的应用程序干净地终止。

How are you supposed to cleanly close Qt apps (assume Qt app source not available)? 你应该如何干净地关闭Qt应用程序(假设Qt应用程序源不可用)?

--------Edit-------- - - - - 编辑 - - - -

Here's some sample code that will reproduce the problem. 这里有一些示例代码可以重现问题。 At the very least, I'd be interested to know if other people see the same problem I'm seeing. 至少,我有兴趣知道其他人是否看到了我所看到的同样问题。

The sample code starts CMake ( download here ), but any Qt app should do. 示例代码启动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;
}

Okay, solved it. 好的,解决了。

The problem comes from the fact that Qt creates a Top-Level window - a QEventDispatcher window. 问题来自于Qt创建了一个顶级窗口 - 一个QEventDispatcher窗口。 Following the procedure outlined by Microsoft, this window gets a WM_CLOSE message, which shuts down that window and its thread. 按照Microsoft概述的过程,此窗口将获取WM_CLOSE消息,该消息将关闭该窗口及其线程。 After that, when the application's main window is closed, no clean up is done and the process remains in system memory. 之后,当应用程序的主窗口关闭时,不会进行清理,并且该过程仍保留在系统内存中。

What's interesting is that by using Task Manager, the QEventDispatcher doesn't get a WM_CLOSE message so stays active, and so when the main window ges a WM_CLOSE message the process exits cleanly. 有趣的是,通过使用任务管理器,QEventDispatcher 不会获得WM_CLOSE消息,因此保持活动状态,因此当主窗口生成WM_CLOSE消息时,该进程将完全退出。 I can only assume that a call to something like IsWindowVisible is being used in the EnumWindowsProc callback in Task Manager, contrary to their documentation . 我只能假设在任务管理器中的EnumWindowsProc回调中使用了类似IsWindowVisible的调用,这与他们的文档相反。 Although that documentation was last reviewed over a decade ago! 虽然该文件是十年前的最后一次审查!

Adding in the call to IsWindowVisible is making the program work with all of the Qt apps, and the other non-Qt apps seem to be happy continuing to work with this change too. 添加对IsWindowVisible的调用使得该程序可以与所有Qt应用程序一起使用,而其他非Qt应用程序似乎也很乐意继续使用此更改。 For completeness, I've included the updated sample code: 为了完整起见,我已经包含了更新的示例代码:

#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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM