【问题标题】:Exceptions silently caught by Windows, how to handle manually?Windows默默捕捉到的异常,如何手动处理?
【发布时间】:2011-02-07 00:13:48
【问题描述】:

当异常在消息泵中抛出时,我们遇到了 Windows 静默处理异常并允许应用程序继续运行的问题。例如,我们创建了一个测试 MFC MDI 应用程序,并覆盖了 OnDraw:

void CTestView::OnDraw(CDC* /*pDC*/)
{
    *(int*)0 = 0; // Crash

    CTestDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    // TODO: add draw code for native data here
}

在运行应用程序时,您可能会收到令人讨厌的错误消息,但实际上您什么也得不到。该程序似乎运行得很好,但是如果您检查输出窗口,您将看到:

第一次机会例外在 Test.exe 中的 0x13929384: 0xC0000005:访问冲突写入 位置 0x00000000.
0x77c6ee42 处的第一次机会异常 在Test.exe:0xC0150010: 激活上下文被停用 对当前线程不活跃 执行。

我知道为什么我会收到应用程序上下文异常,但为什么它被静默处理?这意味着我们的应用程序在使用时可能会遇到严重的问题,但我们永远不会知道,因为我们的用户永远不会报告任何问题。

【问题讨论】:

  • 是的,Visual Studio 2008。
  • 好的,然后在下面查看并尝试我的答案...
  • @Mark Ingram:一旦发生访问冲突,最好让您的进程快速失败。在访问冲突后继续运行会导致细微的内存损坏错误。作为客户,我对偶尔发生的崩溃比给出错误答案更宽容。要记住的事情:)
  • @Billy ONeal,应用程序正在默默地使用异常,然后继续。通过我们自己没有选择。我想要做的是拦截异常并相应地处理它(上面的崩溃应该会导致硬失败,但它不会)。
  • @Mark Ingram:那就继续吧:)

标签: c++ windows mfc exception message-pump


【解决方案1】:

如果您在 x64 操作系统上运行,您可能已经被这个问题困扰了:

http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/

或者(在这种情况下不太可能),它可能是这样的: http://blogs.msdn.com/b/oldnewthing/archive/2011/01/20/10117963.aspx

【讨论】:

【解决方案2】:

您可以强制 Windows 不忽略此代码 sn-p(来自 Microsoft 的 Exceptions that are thrown from an application that runs in a 64-bit version of Windows are ignored)的异常,该代码将放入您的进程代码中:

// my SDK is v6.0A and the two APIs are not available in the .h files, so I need to get them at runtime
#define PROCESS_CALLBACK_FILTER_ENABLED     0x1
typedef BOOL (WINAPI *GETPROCESSUSERMODEEXCEPTIONPOLICY)(__out LPDWORD lpFlags);
typedef BOOL (WINAPI *SETPROCESSUSERMODEEXCEPTIONPOLICY)(__in DWORD dwFlags );
HINSTANCE h = ::LoadLibrary(L"kernel32.dll");
if ( h ) {
   GETPROCESSUSERMODEEXCEPTIONPOLICY GetProcessUserModeExceptionPolicy = reinterpret_cast< GETPROCESSUSERMODEEXCEPTIONPOLICY >( ::GetProcAddress(h, "GetProcessUserModeExceptionPolicy") );
   SETPROCESSUSERMODEEXCEPTIONPOLICY SetProcessUserModeExceptionPolicy = reinterpret_cast< SETPROCESSUSERMODEEXCEPTIONPOLICY >( ::GetProcAddress(h, "SetProcessUserModeExceptionPolicy") );
   if ( GetProcessUserModeExceptionPolicy == 0 || SetProcessUserModeExceptionPolicy == 0 ) {
      return;
   }
   DWORD dwFlags;
   if (GetProcessUserModeExceptionPolicy(&dwFlags)) {
      SetProcessUserModeExceptionPolicy(dwFlags & ~PROCESS_CALLBACK_FILTER_ENABLED); 
   }
}

您可能还必须添加一个unhandled exception filter:过滤器就像一个“顶级异常处理程序”,就像一个最顶层的catch 块。要从 _EXCEPTION_POINTERS 中提取程序员友好的字符串,您可以查看 Is there a function to convert EXCEPTION_POINTERS struct to a string?

LONG WINAPI my_filter(_In_  struct _EXCEPTION_POINTERS *ExceptionInfo)
{
   ::OutputDebugStringA("an exception occured!");
   return EXCEPTION_EXECUTE_HANDLER;
}

您添加过滤器:

::SetUnhandledExceptionFilter(my_filter);

您必须在进程的每个线程中都这样做:虽然之前的 sn-p 是每个进程的,但过滤器是每个线程的。

【讨论】:

  • 我相信这只有在用户的系统上安装了补丁时才有效?对不使用修补程序的用户没有用处。
  • @MarkIngram 恐怕如果用户的计算机上没有安装补丁 kb976038(这意味着,除此之外,kernel32.dll 的正确版本)他将无法看到您的诊断异常情况下的消息。另请参阅 Brian 在stackoverflow.com/questions/2622200/… 的好回答
  • 请注意,上面提到的 976038 修补程序已集成到 Win7 SP1 中,并且此 SetProcessUserModeExceptionPolicy 用于显示消息泵内部引发的异常。这些不会被 C++ try/catch 块捕获,并且需要它们自己的过滤器,如上面的@uvts_cvs 概述。
【解决方案3】:

在浏览了类似的问题后,我偶然发现了这个答案: OpenGL suppresses exceptions in MFC dialog-based application

“好的,我找到了更多信息 对这个。在我的情况下,它是 Windows 7 安装 KiUserCallbackExceptionHandler 作为 异常处理程序,在调用我之前 WndProc 并给我执行 控制。这是由 ntdll!KiUserCallbackDispatcher。一世 怀疑这是一个安全 微软采取措施防止 入侵 SEH。

解决方案是包装你的 wndproc (或 hookproc)与 try/except 框架。”

我已向 Microsoft 提交错误报告,您可以在此处查看他们的回复:
http://connect.microsoft.com/VisualStudio/feedback/details/550944/hardware-exceptions-on-x64-machines-are-silently-caught-in-wndproc-messages

来自微软:

感谢您的报告。我发现这是一个Windows问题, 并且有一个可用的热修复程序。请参见 http://support.microsoft.com/kb/976038 获取可以安装的修复程序 如果你愿意。

【讨论】:

  • 值得注意的是,在大多数情况下,该修补程序毫无意义,因为您的客户不太可能安装它。
  • 但至少有希望将修复程序纳入未来的 Windows 版本/服务包。它胜过“这种行为是设计使然”。 :-)
【解决方案4】:

我遇到了同样的问题,发现这是 Microsoft 错误造成的: http://connect.microsoft.com/VisualStudio/feedback/details/550944/hardware-exceptions-on-x64-machines-are-silently-caught-in-wndproc-messages

Microsoft 提供了一个修复程序,但如果您有多个目标平台,部署它会有些挑战:

http://support.microsoft.com/kb/976038

这是一篇关于描述行为的主题的文章:

http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/

问题基本上是 32 位程序中的硬件异常在 64 位操作系统上的 WndProc 例程中被静默捕获,除非您发送命令告诉它不要这样做。如果您运行的是 Vista SP2,Microsoft 有一个修复程序,需要的,但在 Windows 7 SP1 中不需要(不确定没有 SP 的 Win7)。

即使使用了修补程序,您也需要通过设置注册表项或对内核进行一些调用来告诉它您的进程预计在 WndProc 期间遇到硬件异常时会崩溃,从而启用正确的行为。

根据上面的 PaulBetts 链接,这样做是为了向后兼容 Windows Server 2003。

如果你的程序是 64 位程序,这个问题就会消失。

【讨论】:

  • 请注意,KB976038 已集成到 W7SP1 中,但您仍需要启用异常处理。
【解决方案5】:

以后偶然发现此问题的任何人的事后回答。

这是由 Windows http://support.microsoft.com/kb/976038 中的一个已知问题引起的 - 确保您是最新的,如果需要,请安装热补丁,并将您的应用程序标记为与 Windows 7 兼容。 http://msdn.microsoft.com/en-us/library/dd371711%28v=vs.85%29.aspx

我已经看到了异常代码 c015000f 和 c0150010。

【讨论】:

  • 我遇到过这个问题。我的 Windows 过程回调方法中抛出了一个异常 (std::bad_alloc)。因此,如果我的 WM_NCCREATE 处理程序抛出异常,Windows 会吞下它。提示稍后在一个神秘的位置坠毁....
【解决方案6】:

您的输出看起来像是在使用 Visual Studio...
如果没有忘记我的回答。
您可以指定正常抛出哪些异常,这意味着 Visual Studio 会捕获它们并且您的程序会在发生访问冲突的地方停止。在 Debug/Exceptions... 菜单中执行此操作。如果您不确定要启用什么,只需将它们全部启用...

【讨论】:

  • 当您自己调试时这很好,但当您将产品发布给客户时就不好了。
【解决方案7】:

可能感兴趣的功能:

SetUnhandledExceptionFilter()
_set_invalid_parameter_handler()
_RTC_SetErrorFuncW()
_CrtSetReportHookW2()

PS,请注意 SetUnhandledExceptionFilter() 可以被加载到您的 .exe 中的其他 dll 覆盖。例如,flash 和 nvidia direct3d 就是这样做的。我使用 api hooking 来解决这个问题。

【讨论】:

  • 您好,感谢您的建议,但我已经尝试了所有 4 个(在崩溃前上线)并且仍然没有捕获到异常。
  • 仅仅因为您在 msg 循环之前调用 SetUnhandledExceptionFilter 并不意味着在消息循环期间 dll 不会覆盖它。建议在 SetUnhandledExceptionFilter() 上放置一个断点(在 kernel32 中,而不是在您的 exe 中的调用)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-20
  • 1970-01-01
  • 1970-01-01
  • 2017-08-20
  • 1970-01-01
  • 1970-01-01
  • 2021-02-04
相关资源
最近更新 更多