禁用Windows对子进程错误的自动处理
我正在为一个编译器(LLVM)写测试套件,结果在除了Windows以外的所有平台上都运行得很好。但在Windows上,我遇到了“关键错误处理程序”的消息框,这让测试无法继续。
这个问题让测试变得非常困难,因为在编译器中,问题通常意味着汇编级别的代码无效,因此会出现奇怪且不可预测的错误。
我在寻找解决办法时发现了一个链接,内容是关于如何在Python中捕获Windows的关键错误,而不是让操作系统显示那些讨厌的错误弹窗,但对我来说这个方法不管用。在测试时我仍然会看到这些消息框。
关于 SetErrorMode 的文档中提到:
SEM_FAILCRITICALERRORS:
系统不会显示关键错误处理程序的消息框。相反,系统会将错误发送给调用进程。SEM_NOGPFAULTERRORBOX:
系统不会显示Windows错误报告对话框。每个进程都有一个关联的错误模式,用来告诉系统应用程序将如何响应严重错误。子进程会继承其父进程的错误模式。
然而,在调用 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX)
并使用 CreateProcess
启动进程时,设置了 dwCreationFlags = CREATE_NEW_CONSOLE
,我仍然在子进程失败时看到这些消息框。
如果有帮助的话,我使用的确切Python代码是:
import ctypes
# 3 is SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX
ctypes.windll.kernel32.SetErrorMode(3)
我该如何解决这个问题呢?
2 个回答
错误模式通常会被子进程继承。这听起来像是你用来创建子进程的代码(比如 system
或 execv
)在调用 CreateProcess
时,传递了一个叫 CREATE_DEFAULT_ERROR_MODE
的标志。为了让你想要的效果(继承你设置的错误模式)实现,你需要修改库的实现,确保创建进程的代码不传递这个标志,或者你可以实现一个新的方法,确保它不传递这个标志。
问题其实出在Dr. Watson身上,他无礼地忽视了SetErrorMode。要想阻止Dr. Watson来打扰你,唯一的方法就是让他根本不被调用。这里有两种方法可以做到这一点。
调用
SetUnhandledExceptionHandler(你自己的异常处理函数);
如果在这个处理函数里调用了
exit
,程序就会正常结束,这样Dr. Watson就不会被调用了。UnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { fputs("Application crashed with unhandled exception!\n", stderr); // Exit to prevent anything else from handling this exception. _exit(-3); } int main() { SetUnhandledExceptionFilter(UnhandledExceptionFilter); }
不过,这种方法只适用于你能访问源代码并愿意修改它的情况。在我的例子里,我能访问源代码,但它是几百个独立文件的集合,所以在每个文件里加这段代码根本不现实。而且,这个方法也无法处理在主函数之前发生的错误,或者处理一些根本就不合法的可执行文件。
在调试器下运行进程,使用
CreateProcess(..., DEBUG_PROCESS, ...)
(链接已隐藏) 和WaitForDebugEvent
(链接已隐藏)这样运行进程时,所有的异常都会被传递给调试器,因此可以适当地处理它们。
虽然这个解决方案相对复杂,但这是我找到的唯一能完全阻止Dr. Watson的方法。我目前正在写一个最小化的程序,尝试以正常的方式运行子进程,除了捕获未处理的异常。我会在提交后在这里发布链接。