【问题标题】:How to gracefully terminate a process?如何优雅地终止进程?
【发布时间】:2024-01-23 16:00:02
【问题描述】:

我想终止多个进程,但我想让每个进程都有机会保存其数据,询问用户是否保存文件,甚至忽略关闭请求。

所以TerminateProcess 是不可能的,因为它会立即终止进程。另一种方法是使用SendMessage/PostMessageWM_CLOSE 发送到主窗口,不幸的是我对进程的窗口一无所知,我只有进程ID,所以FindWindow 没有也无济于事。还有其他方法可以找到进程的主窗口吗?

换句话说: 有什么方法可以优雅地终止任何进程,就像您单击“结束任务”时 Windows 7 任务管理器所做的那样? (而不是“结束进程”)

【问题讨论】:

  • 我假设“任务”是*(可见)窗口。因此,WM_CLOSE 将(可能)在终止任务时发送。
  • 如果您不是软件开发人员 - 只是 Windows 用户 - 请参阅 the related Super User question on how to gracefully ask a running application to terminate
  • 请注意,从 Windows 8.1 开始,任务管理器的“结束任务”按钮现在会强制终止所选应用程序并导致您丢失所有未保存的工作。您可能需要编辑您的问题以反映这一点。

标签: winapi process


【解决方案1】:

EnumWindows 枚举进程中的所有*窗口。 GetWindowThreadProcessId获取每个线程的进程和Id。

您现在有足够的信息来正常关闭任何 GUI 应用程序。

您可以将WM_CLOSE 消息发送到您希望关闭的任何窗口。许多窗口处理WM_CLOSE 以提示用户保存文档。您可以使用PostThreadMessage 向发现的线程发送WM_QUIT 消息,以导致消息循环终止。

不允许用户代码从不同的应用程序或线程向 Windows 调用 DestroyWindow...如果应用程序未响应 WM_CLOSEWM_QUIT 请求,您将返回 TerminateProcess 土地。

这不会关闭控制台应用程序,因为应用程序进程和拥有窗口的进程不同。


有关处理控制台应用程序的正确方法,请参阅下面T.s. Arun 的回答。

【讨论】:

  • 当我拥有进程的所有窗口后,是否向每个*窗口发送WM_CLOSE
  • 正是我要说的。 +1。
  • 是 - TaskManager 向应用程序发送 WM_CLOSE 消息。实际上我认为它可能会发送WM_SYSCOMMANDSC_CLOSE 消息。
  • 这是否可以在没有窗口(即控制台)的情况下关闭进程?
  • 进程没有窗口怎么办?假设它是后台驱动程序监控任务,托盘中有一个图标。如果我们自己开发该过程会怎样,这意味着除了将 WM_CLOSE 发送到窗口之外,我们在 Windows API 中还有吗?是使用 Windows Sockets 的唯一方法吗?
【解决方案2】:

我不太确定 win32 api,但您可以通过 shell 执行 taskkill 命令行函数。

taskkill /?
taskkill /pid 1230
taskkill /im notepad.exe

/f 开关会强制终止,但不使用它只会发送终止信号,以便应用程序正常关闭。

【讨论】:

  • 很遗憾,这仅适用于 Windows XP Professional 或更高版本。 (甚至不是 XP 家庭版),但我也需要 Windows 2000 和 XP 家庭版的该功能。但我很想看看那个工具的源代码 :)
【解决方案3】:

请参阅 MSKB Q178893 How To Terminate an Application "Cleanly" in Win32。基本上将发送 WM_CLOSE 发送到目标应用程序的所有窗口以允许宽限关闭,然后使用 TerminateProcess 强制终止应用程序

【讨论】:

  • +1。与 Chris Becke 的解决方案有很多重叠之处,另外还描述了使用 16 位应用程序处理极端情况。不错。
  • 你也应该在这里写一些内容,以防页面被关闭/移动。
  • 仅链接的答案不是好的答案。请按照 MasterMastic 的建议做,并将实质性内容放在这个答案中。
  • 链接当前已关闭。
  • 我认为 MSKB 代码上的许可证不清楚,所以你不会被允许在这里发布。 (因为 SO 帖子目前在 CC BY-SA 4.0 下获得许可)但是这里是有效内容的存档链接:web.archive.org/web/20150221153350/https://…
【解决方案4】:

添加到Chris Becke 回答关于正常终止控制台进程。

AttachConsole() 附加到控制台应用程序并发送控制中断事件,类似于在命令提示符下按 CTRL+C

使用GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,processID)。

此控制事件应在控制台应用程序中处理以正常终止。

【讨论】:

【解决方案5】:

使用 EndTask API 函数。与任务管理器使用的功能相同。

BOOL EndTask(      
    HWND hWnd,
    BOOL fShutDown,
    BOOL fForce
);

http://msdn.microsoft.com/en-us/library/ms633492(VS.85).aspx

【讨论】:

  • 该页面的第一行文字说:[此功能不适用于一般用途。它可能在 Windows 的后续版本中被更改或不可用。]。也许支持者没有点击?
  • @Jon 许多 api 都有这个提示,但是如果不使用它们,很多问题都无法解决。这同样适用于“无证”的。 (我不是说这是其中一种情况)
【解决方案6】:

您可以使用 taskkill /im your_program.exe 向应用程序发送终止信号。它将触发一个 WM_CLOSE 到 windows 消息队列。您可以使用 任何一个 https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx

https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getmessage

处理消息。

请参考类似答案Win32 API analog of sending/catching SIGTERM

【讨论】: