在 Windows 上捕获访问冲突
Catching access violations on Windows
我正在尝试捕获应用程序中所有未处理的异常,以便在它们发生时保存日志文件。这是一个使用 Visual Studio 2013 编译的 64 位 Windows 应用程序,用 C++ 编写。为了进行测试,我使用了 VS 生成的默认 C++ Win32 项目。
我通过使用 SetUnhandledExceptionFilter 注册处理程序来捕获所有异常。这适用于/大多数/情况,但不是全部。所有 throw()-n 异常都会被捕获,大多数硬件异常(如浮点数或访问冲突)也会被捕获。不触发处理程序的代码是:
std::vector<int> foo(5, 0);
for (auto& f : foo)
foo.erase(foo.begin() + 1);
相反,我只是得到标准的 windows 崩溃对话框,而我的异常处理程序没有被调用。但是,如果我 运行 它在 Visual Studio 中附加了调试器,它会正确报告访问冲突异常。其他类型的访问冲突也会触发处理程序:
float* ptr = nullptr;
float value = *ptr;
上面的代码触发异常处理程序。
我也尝试过使用 try/catch 或捕获 SIGSEGV 信号,但第一个示例都没有触发。 abort/terminate 信号也不会被调用。简而言之,当崩溃发生时,我绝不会收到通知。
我想知道在我的应用程序因第一个示例导致的访问冲突而崩溃之前,有什么方法可以在我的应用程序中获得某种通知吗?由于 VS 似乎能够检测到它,我假设有办法。
编辑:
我只是想说明一下,我 运行 在发布模式下编写代码,第一个示例中的错误不是由在调试模式下进行的迭代器越界检查引起的。
编辑2:
我包含了我可以使用 win32 控制台应用程序想出的最简单的示例。在这里查看:
http://pastebin.com/8L1SN5PQ
确保 运行 它处于未附加调试器的发布模式。
您没有看到异常,因为 C 运行时正在内部处理它。具体来说,这是边界检查而不是访问冲突。
运行 在调试器中我发现它是 vector
:
中的第 242 行
_DEBUG_ERROR("vector iterators incompatible");
最终调用_CrtDebugReportW
:https://msdn.microsoft.com/en-us/library/8hyw4sy7.aspx
您可以使用 _CrtSetReportMode
控制 _CrtDebugReportW
的行为。
请注意,这在发布模式下没有影响,因为这些是调试模式边界检查。
这些运行时错误的处理方式不同,它们不会产生 SEH 异常。大致分类在 "programming bug" 和 "malware attack" 之间。如果您没有附加调试器,那么与未捕获的 C++ 异常一样信息丰富,默认处理程序使用 __fastfail().
调用即时死亡
您必须在 main() 函数中调用 _set_invalid_parameter_handler() 来改变它们的处理方式。您可以在您的自定义处理程序中抛出 C++ 异常或调用 RaiseException() 来触发您的 catch-em-all 处理程序或只是在那里报告它们。赞成后者,你想确保进程总是终止。
请注意,您的代码段不是最佳示例。当你在没有启用迭代器调试的情况下构建你的程序时,这是 UB,就像使用默认设置的 Release 构建一样。 UB 不保证您会得到 SEH 异常。如果确实如此,那么您将不得不非常小心地编写您的异常过滤器,它将在堆锁仍然被调用的情况下被调用,因此基本的东西无法工作。最好的方法是用一个命名事件唤醒一个守卫进程。然后进行小型转储并终止程序。
我正在尝试捕获应用程序中所有未处理的异常,以便在它们发生时保存日志文件。这是一个使用 Visual Studio 2013 编译的 64 位 Windows 应用程序,用 C++ 编写。为了进行测试,我使用了 VS 生成的默认 C++ Win32 项目。
我通过使用 SetUnhandledExceptionFilter 注册处理程序来捕获所有异常。这适用于/大多数/情况,但不是全部。所有 throw()-n 异常都会被捕获,大多数硬件异常(如浮点数或访问冲突)也会被捕获。不触发处理程序的代码是:
std::vector<int> foo(5, 0);
for (auto& f : foo)
foo.erase(foo.begin() + 1);
相反,我只是得到标准的 windows 崩溃对话框,而我的异常处理程序没有被调用。但是,如果我 运行 它在 Visual Studio 中附加了调试器,它会正确报告访问冲突异常。其他类型的访问冲突也会触发处理程序:
float* ptr = nullptr;
float value = *ptr;
上面的代码触发异常处理程序。
我也尝试过使用 try/catch 或捕获 SIGSEGV 信号,但第一个示例都没有触发。 abort/terminate 信号也不会被调用。简而言之,当崩溃发生时,我绝不会收到通知。
我想知道在我的应用程序因第一个示例导致的访问冲突而崩溃之前,有什么方法可以在我的应用程序中获得某种通知吗?由于 VS 似乎能够检测到它,我假设有办法。
编辑: 我只是想说明一下,我 运行 在发布模式下编写代码,第一个示例中的错误不是由在调试模式下进行的迭代器越界检查引起的。
编辑2: 我包含了我可以使用 win32 控制台应用程序想出的最简单的示例。在这里查看: http://pastebin.com/8L1SN5PQ
确保 运行 它处于未附加调试器的发布模式。
您没有看到异常,因为 C 运行时正在内部处理它。具体来说,这是边界检查而不是访问冲突。
运行 在调试器中我发现它是 vector
:
_DEBUG_ERROR("vector iterators incompatible");
最终调用_CrtDebugReportW
:https://msdn.microsoft.com/en-us/library/8hyw4sy7.aspx
您可以使用 _CrtSetReportMode
控制 _CrtDebugReportW
的行为。
请注意,这在发布模式下没有影响,因为这些是调试模式边界检查。
这些运行时错误的处理方式不同,它们不会产生 SEH 异常。大致分类在 "programming bug" 和 "malware attack" 之间。如果您没有附加调试器,那么与未捕获的 C++ 异常一样信息丰富,默认处理程序使用 __fastfail().
调用即时死亡您必须在 main() 函数中调用 _set_invalid_parameter_handler() 来改变它们的处理方式。您可以在您的自定义处理程序中抛出 C++ 异常或调用 RaiseException() 来触发您的 catch-em-all 处理程序或只是在那里报告它们。赞成后者,你想确保进程总是终止。
请注意,您的代码段不是最佳示例。当你在没有启用迭代器调试的情况下构建你的程序时,这是 UB,就像使用默认设置的 Release 构建一样。 UB 不保证您会得到 SEH 异常。如果确实如此,那么您将不得不非常小心地编写您的异常过滤器,它将在堆锁仍然被调用的情况下被调用,因此基本的东西无法工作。最好的方法是用一个命名事件唤醒一个守卫进程。然后进行小型转储并终止程序。