在 Windows 中发送任意信号?

39

Linux支持使用kill命令向进程发送任意的Posix-Signal,例如SIGINTSIGTERM。虽然SIGINTSIGTERM只是一种友好或不那么友好的结束进程的老方法,但SIGQUIT用于触发核心转储。这可用于触发正在运行的Java VM打印线程转储,包括所有运行线程的堆栈跟踪 -- 很棒!在打印调试信息后,Java VM将继续执行它之前正在做的事情;实际上,线程转储只是以最高优先级的另一个派生线程进行的。(您可以使用kill -3 <VM-PID>来尝试自己执行此操作。)

请注意,您还可以使用sun.misc包中的(不受支持的!)SignalSignalHandler类注册自己的信号处理程序,因此您可以尽情享受其中的乐趣。

然而,我还没有找到一种在Windows进程中发送信号的方法。某些用户输入会创建信号:例如Ctrl-C在两个平台上都会触发SIGINT。但是,在Windows上似乎没有任何实用程序可以手动向正在运行但非交互式进程发送信号。显然的解决方案是使用Cygwin的kill可执行文件,但是尽管它可以使用适当的Windows API结束Windows进程,但我无法使用它发送SIGBREAK(相当于SIGQUIT的Windows版本);实际上,我认为它能够发送到Windows进程的唯一信号是SIGTERM

因此,长话短说并重申标题:我如何在Windows中向进程发送任意信号?


Sun公司警告不要使用com.sun层次结构中的类,因为它们可能(并且确实)会在版本之间甚至平台之间任意更改。 - Powerlord
1
我知道这一点,这就是为什么我写了不支持(感叹号)。这只是作为旁注,并不是问题的核心。 - morsch
我想自己生成一个堆栈转储...太糟糕了,Sun没有在Java托盘图标中添加这个功能。 - Jeach
7个回答

19

如果您想以明确/编程方式杀死另一个程序/进程,可以使用SysInternals的pstools中的一个名为"pskill"的小工具,其行为与Unixen "kill"完全相同。

如果您想要其他内容,请继续阅读(尽管我可能在下面的一些具体内容上有所错误-自从我最后只使用WinAPI和Charles Petzold的优秀书籍“Windows编程”作为指南开发了Windows程序以来,已经过去了很长时间)。

在Windows上,您没有适当的“信号”,WinMain和WinProc函数从操作系统接收到的是简单的消息。例如,当单击窗口的“X”按钮时,Windows会向该窗口处理程序发送消息WM_CLOSE。当窗口被删除但程序仍在运行时,它会发送WM_DESTROY。当要退出主消息处理循环时,WinMain(而不是WinProc)接收WM_QUIT。您的程序应该按预期响应所有这些-实际上,您可以通过不执行应该在收到WM_CLOSE时执行的操作来开发“不可关闭”的应用程序。

当用户从Windows任务管理器中选择任务并单击“结束任务”时,操作系统将发送WM_CLOSE(和我不记得的另一个消息)。但是,如果使用“结束进程”,则直接杀死进程,永远不会发送任何消息(来源:The Old New Thing)。

我记得有一种方法可以获取另一个进程窗口的HWND,一旦您获得了它,另一个进程可以通过PostMessage和DispatchMessage函数向该窗口发送消息。


我认为Windows也不会发送WM_QUIT消息,至少在关闭窗口时不会发送。 - Vladimir Panteleev
啊,你让我研究这个...这总是一件好事,因为我已经很久没有直接使用Win API进行开发了。现在我对它更清楚了。 - Joe Pineda
这只是 UI 应用程序的行为 - 如果您的应用程序没有 UI(例如控制台应用程序),则此答案不正确。在没有 UI 的情况下实现本机关闭或关闭钩子的一种可能方法是 https://msdn.microsoft.com/de-de/library/windows/desktop/ms686016(v=vs.85).aspx。 - specializt

9

Windows不是POSIX系统,它没有信号(signal)机制。控制台程序能收到的唯一“信号”是如果调用 SetConsoleCtrlHandler 函数,在这种情况下,可以通知程序用户已按下 Ctrl+C、Ctrl+Break、关闭了控制台窗口、注销或关闭系统。

其它所有内容都通过IPC实现,通常使用窗口消息或RPC。检查Sun公司的文档以确定在Windows JRE上是否有实现您要求的功能的方法。


4
在Windows中,一切都围绕着Win32消息。我不认为有一个命令行工具可以做到这一点,但是在C++中,您可以使用FindWindow向另一个Windows程序发送任意消息。例如:
#define WM_MYMSG  ( WM_USER+0x100 )
HWND h = ::FindWindow(NULL,_T("Win32App"));
if (h) {
    ::PostMessage(h, WM_MYMSG, 0, 0);
}

这也可以使用com互操作在C#中完成。


3

Windows-kill代码库似乎已被劫持;但是,我能够通过Web Archive找到最新版本的源代码副本:https://web.archive.org/web/20200910171157if_/https://codeload.github.com/alirdn/windows-kill/tar.gz/1.1.4 - Nathan Osman
@NathanOsman 发现了另一个存储库,看起来是原始存储库的完全副本,我将此链接添加到了我的答案中。同时感谢您添加了您的Web Archive链接到我的答案中。此外还存在网络档案馆的二进制发布版本。 - Arty

1
您也可以使用jconsole查看所有运行线程的堆栈跟踪。这适用于Windows和任何支持Java的其他操作系统。Jconsole还具有许多其他不错的功能,如内存图形、CPU图形等。
它虽然没有回答您最初的问题,但希望能让您获得相同的结果。
如果您不熟悉jconsole,请查看 使用JConsole文档。

0

我只是想知道,现在微软拥有的SysInternals中的PsTools是否会对您有所帮助。


1
对我没用(pskill不像Ctrl+C或Ctrl+Break那样工作...) - dedek

0

Ruby 在 Windows 上能够(至少模拟)捕获 SIGINT、SIGKILL 等信号,并对这些消息进行处理。建议查看一下。

在 Windows 上,Ruby 是通过调用 TerminateProcess 或等效方法来实现“向该进程发送 SIGINT 信号”的。

还有一个 Windows 等效的方法可以“捕获 ctrl+c”,我想那就是它所调用的方法。


如何的一个例子? - Jaime Hablutzel
好的,我添加了更多内容,或者你想要一个Ruby示例吗? :) - rogerdpack

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