Windows中崩溃进程的可预测退出代码

对于在Windows中正常退出的进程,进程的退出代码通常是来自main的返回值或传递给std::exit的退出代码。 然后可以使用%ERRORLEVEL%来查询退出代码,这可以用来确定程序是否正确执行,或者是否有一些exception的input/失败指示特定的问题(特定于应用程序)。

但是,如果进程崩溃,我对退出代码感兴趣。 举一个非常简单的示例程序:

 int main() { int * a = nullptr; *a = 0xBAD; return 0; } 

当我编译并在Windows中运行时,在命令行上,我得到:

 MyCrashProgram.exe -> crashes echo %ERRORLEVEL% -> -1073741819 

退出代码始终是这个数字。 这导致了我几个问题:

  • 退出代码-1073741819基于无效写入崩溃以某种方式可预测?
  • 如果是这样,有没有一些方法来确定基于退出代码的崩溃types?
  • 这是否改变了使用的编译器(我使用MSVC 2012)?
  • 这是否改变与正在使用的Windows版本(我使用Win10 TP)?
  • 这是否会改变与架构(例如x64 – 我使用Win32的)?

请注意,我不感兴趣如何修改程序来捕捉exception。 我感兴趣的是对现有程序中可能发生的崩溃进行分类,我可能无法修改。

Solutions Collecting From Web of "Windows中崩溃进程的可预测退出代码"

关于STATUS_ACCESS_VIOLATION的评论,让我看到了关于GetExceptionCode的文档:

返回值标识异常的类型。 下表列出了由于常见编程错误而可能发生的异常代码。 这些值在WinBase.h和WinNT.h中定义。

EXCEPTION_ACCESS_VIOLATION映射到下面的列表中的STATUS_ACCESS_VIOLATION 。 以STATUS为前缀的列表中的所有异常都直接定义为以EXCEPTION为前缀的异常代码。 在RaiseException的文档RaiseException ,它解释了在发生异常时尝试调试异常的过程,最后一步是:

如果进程没有被调试,或者相关的调试器没有处理异常,则系统根据异常类型提供默认处理。 对于大多数例外情况,默认操作是调用ExitProcess函数。

所以要回答我的问题:

  • 是的,退出代码是可预测的,它映射到EXCEPTION_STATUS_VIOLATION
  • 其他类型的错误将映射到其他常见的异常代码。 但是,通过使用任意异常代码(未处理)调用RaiseException,流程的退出代码可以是任何
  • 退出代码依赖于Windows SDK,而不是编译器,执行Windows版本或体系结构。 虽然这在理论上可能会随着更新的Windows SDK而发生变化,但这对于向后兼容性来说是不太可能的。

这不是一个全面的答案,而是一些提示,以便你可以前进。

我认为没有办法自动区分所有可能的事故原因。 要做到这一点,你将不得不自己捕捉错误,并提供自己的退出代码

为了捕获所有可能的(可捕获的)错误,你必须设置异常和信号处理程序。 这是因为访问违规是linux下的windows和signal(SIGSEV)下的异常。

看到这个问题的细节关于Windows上的不同类型的错误: 捕获访问冲突异常

这里是另一个在linux上进行信号处理的线程

这里是Raymond Chen (重点是我的)的相关短篇博文 :

流程退出代码没有标准。 你可以传递你想要的任何东西给ExitProcess,这就是GetExitCodeProcess将会返回的东西。 内核没有解释这个值。 如果你想让代码42的意思是“发生了一些无限可能”,那么给你更多的权力。

然而,有一个约定,零的退出码意味着成功(尽管什么构成“成功”由程序的作者自行决定),非零的退出码意味着失败(再次,细节留给自由裁量权的程序员)。 通常,较高的退出代码值表示更严重的失败类型。 命令处理器ERRORLEVEL关键字是根据这些惯例而设计的。

有些情况下,你的进程将会处于一个糟糕的状态,以至于一个组件会自行终止进程。 例如,如果一个进程找不到从其导入的DLL,或者其中一个DLL未能初始化, 那么加载器将终止进程并使用状态代码作为进程退出代码。 我相信,当一个程序崩溃,由于一个未处理的异常,异常代码被用作退出代码。

一个客户看到他们的程序崩溃,退出代码为3,无法找出它来自哪里。 他们从不在程序中使用退出代码。 最终,确定了幻数3​​的来源: C运行时终止函数以退出代码3终止进程