Windows默默捕获的异常,如何手动处理?

时间:2010-04-12 12:59:45

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

我们遇到的问题是,当消息泵内部抛出异常时,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:   激活上下文被停用   当前线程无效   执行。

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

7 个答案:

答案 0 :(得分:12)

如果你在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

答案 1 :(得分:11)

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

  

“好的,我发现了更多信息   对这个。就我而言,它是Windows 7   安装   KiUserCallbackExceptionHandler为   异常处理程序,在调用我之前   WndProc并给我执行   控制。这是通过   NTDLL!KiUserCallbackDispatcher。一世   怀疑这是一个安全   微软为防止采取的措施   入侵SEH。

     

解决方案是包装你的wndproc   (或hookproc)与try / except   帧“。

我已经向微软提交了一份错误报告,你可以在这里看到他们的回复:
http://connect.microsoft.com/VisualStudio/feedback/details/550944/hardware-exceptions-on-x64-machines-are-silently-caught-in-wndproc-messages

来自Microsoft:

  

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

答案 2 :(得分:4)

可能感兴趣的功能:

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

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

答案 3 :(得分: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位程序,则此问题就会消失。

答案 4 :(得分:3)

对于那些后来偶然发现此事的人来说,这是一个很好的答案。

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

我见过这个例外代码为c015000f和c0150010。

答案 5 :(得分:2)

您可以强制Windows不要忽略您将放入流程代码中的此代码段(来自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);

并且你必须在你的进程的每个线程中执行它:虽然前面的代码片段是每个进程,但是过滤器是每个线程的。

答案 6 :(得分:-1)

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