【问题标题】:Catching access violations on Windows在 Windows 上捕获访问冲突
【发布时间】:2016-01-07 08:08:58
【问题描述】:

我试图在我的应用程序中捕获所有未处理的异常,以便在它们发生时保存一个日志文件。这是使用 C++ 编写的 Visual Studio 2013 编译的 64 位 Windows 应用程序。为了测试,我使用的是 VS 生成的默认 C++ Win32 项目。

我通过使用 SetUnhandledExceptionFilter 注册处理程序来捕获所有异常。这适用于 /most/ 情况,但不是全部。捕获所有 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 信号,但都没有被第一个示例触发。中止/终止信号也不会被调用。简而言之,当崩溃发生时,我不会收到任何通知。

我想知道在我的应用程序由于第一个示例导致的访问冲突而崩溃之前,有什么方法可以在我的应用程序中获得某种通知?由于 VS 似乎能够检测到它,我假设有办法。

编辑: 我只是想明确一下,我在发布模式下运行代码,第一个示例中的错误不是由在调试模式下完成的迭代器越界检查引起的。

编辑2: 我包括了使用 win32 控制台应用程序可以想出的最简单的示例。在这里看到它: http://pastebin.com/8L1SN5PQ

确保在没有附加调试器的情况下以发布模式运行它。

【问题讨论】:

  • 您需要查找 SEH 异常。这些不是正常 c++ 异常适用的情况。
  • 我试过了。在 Visual Studio 中启用 SEH 的情况下使用 SEH __try/__except 会产生相同的结果。第一个示例不会触发异常,第二个会触发。
  • 既然,正如汉斯所说,这不是一个好主意,我不会再花时间帮你做这件事了。相反:如果您正在调试,请在调试版本中运行应用程序并使用工具对其进行调试,直到没有错误为止。如果它是一个实时应用程序,并且您正在尝试调试偶尔发生的崩溃,请考虑部署调试版本或使用遥测服务。
  • 感谢您迄今为止的帮助。遗憾的是,我无法向我们的用户分发调试版本,并且正在构建的产品非常庞大,因此肯定很难找到只有运行发布代码的用户才能找到的问题,我想涵盖所有基础并拥有错误报告至少包含调试日志。

标签: c++ windows visual-studio exception access-violation


【解决方案1】:

这些运行时错误的处理方式不同,它们不会产生 SEH 异常。大致分为“编程错误”和“恶意软件攻击”之间的某处。如果您没有附加调试器,则与未捕获的 C++ 异常一样提供信息,默认处理程序使用 __fastfail() 调用即时死亡。

您必须在 main() 函数中调用 _set_invalid_parameter_handler() 来更改它们的处理方式。您可以在自定义处理程序中抛出 C++ 异常,或调用 RaiseException() 来触发您的 catch-em-all 处理程序,或者直接在此处报告它们。支持后者,您要确保进程始终终止。

请注意,您的 sn-p 可能不是最好的示例。当您在没有启用迭代器调试的情况下构建程序时,这是 UB,例如使用默认设置的 Release 构建。 UB 不保证你会得到一个 SEH 异常。如果确实如此,那么您将不得不非常仔细地编写您的异常过滤器,它将在堆锁仍然被占用的情况下被调用,因此基本的东西无法工作。最好的方法是用一个命名事件唤醒一个守卫进程。然后它会进行一个小型转储并终止程序。

【讨论】:

  • 谢谢。我试过注册这样的处理程序:“void _invalid_parameter(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved) { MessageBox(NULL, L"InvalidParameter", L"InvalidParameter" , MB_OK); }" 在 WinMain 中调用:"_set_invalid_parameter_handler(_invalid_parameter);"但是行为没有改变。 (即处理程序似乎没有被调用)
  • 当我尝试使用您发布的 sn-p 时效果很好。一定要避免迭代器调试功能有帮助,当它显示一个断言对话框时单击“忽略”。
  • 它在调试模式下工作,并且为我附加了调试器。但是在没有调试器的情况下运行是我遇到问题的地方。
  • 在发布版本中,您将无法从迭代器调试功能中获得帮助,这将导致访问冲突 SEH 以正常方式死亡。通常,UB 没有任何保证。您的正常异常过滤器会看到哪个。
  • 在发布模式下运行它(使用调试器)我看到我们发生了堆损坏。循环运行大约五次而不是立即失败(因为在释放模式下忽略了边界检查)。我试过 __try/__except,这确实有效,但是堆损坏意味着当我尝试 printf 或其他任何东西时,由于无法分配内存而引发进一步的异常。
【解决方案2】:

您没有看到异常,因为它是由 C 运行时在内部处理的。具体来说,它是边界检查而不是访问冲突。

在调试器中运行它,我发现它是vector 中的第 242 行:

            _DEBUG_ERROR("vector iterators incompatible");

最终调用_CrtDebugReportWhttps://msdn.microsoft.com/en-us/library/8hyw4sy7.aspx

您可以使用_CrtSetReportMode 控制_CrtDebugReportW 的行为。

请注意,这在发布模式下无效,因为这些是调试模式边界检查。

【讨论】:

  • 谢谢。我试过设置“_set_error_mode(_OUT_TO_MSGBOX);”在 WinMain 中,但行为保持不变。有什么我做错了吗?
  • @user2953192 sorry _set_error_mode 控制不同类型的错误。我很抱歉。在你的情况下,你想要_CrtSetReportMode
猜你喜欢
  • 2010-10-02
  • 1970-01-01
  • 2022-01-07
  • 2013-05-12
  • 2010-11-01
  • 2010-12-02
  • 1970-01-01
  • 2020-11-18
相关资源
最近更新 更多