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

发布于 2024-08-28 08:45:46 字数 684 浏览 9 评论 0原文

当异常在消息泵内抛出时,我们遇到了 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: 激活上下文被停用 对于当前线程不活跃 执行。

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

We're having problems with Windows silently eating exceptions and allowing the application to continue running, when the exception is thrown inside the message pump. For example, we created a test MFC MDI application, and overrode 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
}

You would expect a nasty error message when running the application, but you actually get nothing at all. The program appears to be running perfectly well, but if you check the output window you will see:

First-chance exception at
0x13929384 in Test.exe:
0xC0000005: Access violation writing
location 0x00000000.
First-chance exception at 0x77c6ee42
in Test.exe: 0xC0150010: The
activation context being deactivated
is not active for the current thread
of execution.

I know why I'm receiving the application context exception, but why is it being handled silently? It means our applications could be suffering serious problems when in use, but we'll never know about it, because our users will never report any problems.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

无力看清 2024-09-04 08:45:47

在浏览类似的问题后,我偶然发现了这个答案:
OpenGL 抑制基于 MFC 对话框的应用程序中的异常

“好的,我找到了更多信息
关于这个。就我而言,它是 Windows 7
安装的
KiUserCallbackExceptionHandler 作为
异常处理程序,在调用我的之前
WndProc 并给我执行
控制。这是由
ntdll!KiUserCallbackDispatcher。我
怀疑这是一个安全
Microsoft 采取的防止措施
侵入 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

来自 Microsoft:

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

After browsing similar questions I stumbled across this answer:
OpenGL suppresses exceptions in MFC dialog-based application

"Ok, I found out some more information
about this. In my case it's windows 7
that installs
KiUserCallbackExceptionHandler as
exception handler, before calling my
WndProc and giving me execution
control. This is done by
ntdll!KiUserCallbackDispatcher. I
suspect that this is a security
measure taken by Microsoft to prevent
hacking into SEH.

The solution is to wrap your wndproc
(or hookproc) with a try/except
frame."

I've filed a bug report with Microsoft, you can see their response here:
http://connect.microsoft.com/VisualStudio/feedback/details/550944/hardware-exceptions-on-x64-machines-are-silently-caught-in-wndproc-messages

From Microsoft:

Thanks for the report. I've found out that this is a Windows issue,
and there is a hot fix available. Please see
http://support.microsoft.com/kb/976038 for a fix that you can install
if you wish.

旧人九事 2024-09-04 08:45:47

可能感兴趣的函数:

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

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

functions that may be of interest:

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

PS, be aware that SetUnhandledExceptionFilter() can be overriden by other dlls loaded into your .exe. eg, flash and nvidia direct3d do this. I use api hooking to cure this.

倾`听者〃 2024-09-04 08:45:47

我遇到了同样的问题,发现这是 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 例程中被静默捕获,除非你发送命令告诉它不要这样做。 Microsoft 针对此问题提供了一个修补程序,如果您运行的是 Vista SP2,则需要该修补程序,但 Windows 7 SP1 则不需要该修补程序(不确定不带 SP 的 Win7 是否如此)。

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

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

如果您的程序是 64 位程序,则此问题就会消失。

I experienced this same issue, and found it was a result of this Microsoft bug:
http://connect.microsoft.com/VisualStudio/feedback/details/550944/hardware-exceptions-on-x64-machines-are-silently-caught-in-wndproc-messages

There’s a fix available from Microsoft, though deploying it is somewhat challenging if you have multiple target platforms:

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

Here's an article on the subject describing the behavior:

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

The issue is basically that Hardware exceptions in 32-bit programs are silently caught in the WndProc routine on 64-bit OSs, unless you send commands telling it not to. Microsoft has a hotfix for the issue that is required if you're running Vista SP2, but isn't required with Windows 7 SP1 (not sure about Win7 without the SP).

Even WITH the hotfix, you need to enable the correct behavior by setting a registry key or making some calls to the kernel to tell it your process expects hardware exceptions to crash when encountered during WndProc.

According to the PaulBetts link above, this was done for backwards compatibility with Windows Server 2003.

If you program is a 64-bit program, this issue goes away.

愁以何悠 2024-09-04 08:45:47

您可以使用此代码片段强制 Windows 不忽略异常(来自 Microsoft 的 从应用程序抛出的异常在 64 位版本的 Windows 中运行的内容将被忽略),您将把它放入进程代码中:

// 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); 
   }
}

您可能还必须添加 未处理的异常过滤器:过滤器的作用类似于“顶级异常处理程序”最上面的 catch 块。要从 _EXCEPTION_POINTERS 中提取程序员友好的字符串,您可以看到 是否有一个函数可以将 EXCEPTION_POINTERS 结构转换为字符串?

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

您添加过滤器:

::SetUnhandledExceptionFilter(my_filter);

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

You can force Windows to not ignore the exceptions with this code snippet (from Microsoft's Exceptions that are thrown from an application that runs in a 64-bit version of Windows are ignored) that you will put in your process code:

// 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); 
   }
}

It may be you have to add also an unhandled exception filter: the filter acts like a "top level exception handler" that is like a topmost catch block. For extracting a programmer-friendly string from _EXCEPTION_POINTERS you can see 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;
}

You add the filter with:

::SetUnhandledExceptionFilter(my_filter);

and you have to do it in every threads of your process: while the previous snippet is per-process, the filter is per-thread.

指尖微凉心微凉 2024-09-04 08:45:47

对于后来偶然发现这一点的人来说,事后回答。

这是由 Windows http://support.microsoft.com/kb/976038 - 确保您处于最新状态,根据需要安装热补丁,并将您的应用程序标记为与 Windows 7 兼容。 http://msdn.microsoft.com/en -us/library/dd371711%28v=vs.85%29.aspx

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

ANSWER IN HINDSIGHT for anyone who stumbles upon this later.

This was caused by a known issue in Windows http://support.microsoft.com/kb/976038 - make sure you're up to date, install the hotpatch if you need to, and mark your application as Windows 7 compatible. http://msdn.microsoft.com/en-us/library/dd371711%28v=vs.85%29.aspx

I've seen this with exception codes c015000f and c0150010.

相对绾红妆 2024-09-04 08:45:47

您的输出看起来像是您正在使用 Visual Studio...

如果没有忘记我的回答。

您可以指定正常抛出哪些异常,这意味着 Visual Studio 会捕获它们,并且您的程序会在发生访问冲突的地方停止。在“调试/异常...”菜单中执行此操作。如果您不确定要启用什么,只需将它们全部启用...

Your output looks like you're using Visual Studio...

If not forget about my answer.

You can specify which exceptions will be thrown normally, meaning that Visual Studio catches them and your progam stops where the access violation occurred. Do this in the Debug/Exceptions... menu. If you are not sure what to enable, just enable them all...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文