优雅地终止一个进程的方法是什么?

46

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

所以,TerminateProcess不适用,因为它会立即杀掉进程。另一种方法是使用SendMessage/PostMessage发送WM_CLOSE消息到主窗口,但很遗憾,我对进程的窗口一无所知,我只有进程ID,所以FindWindow也没用。有没有其他办法找到进程的主窗口?

换句话说:是否有任何方式像Windows 7任务管理器中单击“结束任务”时那样优雅地终止任何进程?(而不是“结束进程”)


我假设“任务”是顶级(可见)窗口。因此,WM_CLOSE消息将在终止任务时发送(可能)。 - Nick Dandoulakis
1
如果您不是软件开发人员,只是Windows用户,请参阅相关的Super User问题,了解如何优雅地请求终止正在运行的应用程序 - unforgettableidSupportsMonica
1
请注意,从Windows 8.1开始,任务管理器的“结束任务”按钮现在会强制终止所选择的应用程序,并导致您丢失任何未保存的工作。您可能需要编辑您的问题以反映这一点。 - unforgettableidSupportsMonica
"结束任务"按钮是关于窗口的,而不是进程。"结束进程"按钮是关于进程的。两者是不同的东西,并且位于不同的选项卡下。一个进程可以有零个到多个窗口。 - AmigoJack
"结束任务"按钮是关于窗口的,而不是进程。"结束进程"按钮是关于进程的。两者是不同的事物,并位于不同的选项卡下。一个进程可以有零到多个窗口。 - undefined
6个回答

31

EnumWindows 可以列举出一个进程中所有的顶层窗口。 GetWindowThreadProcessId 可以获得每个线程所在的进程和 ID。

有了这些信息,你就可以优雅地关闭任何 GUI 应用程序了。

你可以向任何你想要关闭的窗口发送 WM_CLOSE 消息。许多窗口会通过处理 WM_CLOSE 消息来提示用户保存文档。使用 PostThreadMessage 向发现的线程发送 WM_QUIT 消息,以使消息循环终止。

用户代码不允许从不同的应用程序或线程调用 DestroyWindow 来销毁窗口...... 如果该应用程序不响应 WM_CLOSEWM_QUIT 请求,则需要使用 TerminateProcess 函数。

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


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


当我拥有进程的所有窗口时,我是否需要向每个顶层窗口发送 WM_CLOSE - Daniel Rikowski
2
是的 - TaskManager向应用程序发送WM_CLOSE消息。实际上,我认为它可能会发送WM_SYSCOMMANDSC_CLOSE消息。 - Chris Becke
6
这个工具可以关闭进程而不需要窗口,例如命令行窗口? - rpattabi
4
如果一个进程没有任何窗口怎么办?比方说,它是一个后台驱动程序监控任务,在系统托盘中有一个图标。如果我们自己开发这个进程,除了向一个窗口发送WM_CLOSE之外,在Windows API中还有什么其他的方法可用吗?是不是唯一的方法是使用Windows Sockets? - blackbada_cpp
FarManager(这是一款流行的控制台应用程序)显然有一个特殊的窗口来处理此类事件:https://github.com/FarGroup/FarManager/blob/0871bcdcbb7ca8dcbb6155edfbd5a5d426e5cccd/far/wm_listener.cpp#L53Windows套接字是个坏主意,使用命名事件 - https://learn.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createeventw(带有非空lpname)。通常使用GUID作为此类事件名称。 - nponeccop

21

我对win32 API不是很确定,但你可以使用shell执行taskkill命令行函数。

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

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


5
很遗憾,这只适用于Windows XP专业版或更高版本,(甚至不适用于XP家庭版),但我也需要在Windows 2000和XP家庭版上使用该功能。不过,我很想看到那个工具的源代码 :) - Daniel Rikowski
开始一个新的流程与终止一个流程相反。仅仅运行程序并不等同于“编程” - 这个答案适用于Super User,而不是Stack Overflow。 - AmigoJack
1
@AmigoJack 这个网站是关于帮助程序员的,但不一定意味着要为他们编写代码,所以没有说这个答案不适合在这里。你可以争论是否是正确的方法,但在编程语言中执行 shell 命令通常只需要一行代码,有时是完成任务的最简单方式。taskkill 是可预测且得到良好支持的,你可以把它(和其他内置的 shell 命令)看作是一个通用函数。 - Andy E
1
然而,如果你在编写脚本的话,这可能是你会选择的方式,因为Win32 API并不一定可用。同样地,对于其他一些脚本语言来说,这是一个快速简单的选项,适用于大多数情况。我理解你的观点,这可能不是最好的方式,但使用另一个专门用于终止进程的进程来终止进程,并没有什么“讽刺”的地方。Win32 API也并非完美无缺。例如,初步的谷歌搜索表明,它无法终止系统进程。 - Andy E
1
然而,如果你在编写脚本的话,这可能就是你会选择的方式,因为Win32 API并不一定可用。同样,对于其他一些脚本语言来说,这是一个快速简单的选项,适用于大多数情况。我理解你的观点,这可能并不是最佳方式,但通过使用另一个专门用于终止进程的进程来终止进程,并没有什么“讽刺”的。Win32 API 也并非没有缺点。例如,初步的谷歌搜索表明,使用它无法终止系统进程。 - undefined
显示剩余5条评论

14

+1。与Chris Becke的解决方案有很多重叠之处,还描述了如何处理16位应用程序的特殊情况。不错。 - j_random_hacker
12
你也应该在这里写一些内容,以防页面被关闭/移动。 - MasterMastic
3
目前链接无法使用。 - owacoder
我认为 MSKB 代码的许可证不太清楚,所以您不能在这里发布它(因为 SO 帖子目前是根据 CC BY-SA 4.0 许可证授权的)。但是,这里有一个有效的内容存档链接:https://web.archive.org/web/20150221153350/https://support.microsoft.com/kb/178893 - WebFreak001

6

1
以下是此技术的实际实现示例(C#):https://github.com/gapotchenko/Gapotchenko.FX/blob/1accd5c03a310a925939ee55a9bd3055dadb4baa/Source/Gapotchenko.FX.Diagnostics.Process/ProcessExtensions.End.cs#L247-L328 - ogggre

3

11
该页面的第一行文本说:“[此功能不适用于一般使用。在后续版本的Windows中,它可能会被更改或不可用。]”。也许点赞者没有点击进去看? - Jon
4
@Jon 许多API都有这个提示,但许多问题不能在不使用它们的情况下解决。对于“未记录”的API也是如此。(我不是说这是其中的一个案例) - Mauro H. Leggieri
任务管理器不使用EndTask。相反,它是一个相当复杂的代码,首先尝试通过发送消息优雅地关闭进程,然后作为最后的手段调用TerminateProcess()。 - tigrou

-1

开始一个新的进程是终止一个进程的相反。仅仅运行程序并不等同于“编程” - 这个答案适用于 Super User,而不是 Stack Overflow。 - AmigoJack
开始一个新的流程是终止一个流程的相反。仅仅运行程序并不是“编程”——这个答案更适合Super User,而不是Stack Overflow。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接