Winapi 为什么Windows在WM_关闭期间会吞下异常

Winapi 为什么Windows在WM_关闭期间会吞下异常,winapi,exception-handling,Winapi,Exception Handling,当我试图弄清楚为什么我正在开发的应用程序无法关闭时,我意识到它在其WM\u close处理程序中抛出了一个异常。然而,应用程序并没有崩溃(它应该崩溃),异常会被默默地忽略 为了确保没有其他事情发生,我在VisualStudio中创建了一个新的C++ Win32应用程序,并添加了: case WM_CLOSE: (*(int*)NULL) = 0; break; 同样的事情:没有崩溃,只是调试日志中第一次出现异常。如果我将相同的代码添加到WM_命令处理程序中,它会按预期崩溃 所以

当我试图弄清楚为什么我正在开发的应用程序无法关闭时,我意识到它在其
WM\u close
处理程序中抛出了一个异常。然而,应用程序并没有崩溃(它应该崩溃),异常会被默默地忽略

<>为了确保没有其他事情发生,我在VisualStudio中创建了一个新的C++ Win32应用程序,并添加了:

case WM_CLOSE:
    (*(int*)NULL) = 0;
    break;
同样的事情:没有崩溃,只是调试日志中第一次出现异常。如果我将相同的代码添加到
WM_命令
处理程序中,它会按预期崩溃

所以我很好奇:
WM_CLOSE
有什么特别之处,以至于Windows认为它抛出的异常应该被忽略


(顺便说一句:这是在运行x86程序的Windows 7 x64上)

Win 64上的
WindowProc
会吞噬异常是预期行为。()

也可以在博客上看到更好的解释,尤其是他的笔记

为什么这种情况不会一直发生? 这就是为什么这种情况似乎只发生在某些窗口消息上—— 请记住,窗口消息可以来自不同的来源, 任何人(*)都可以将消息排入窗口队列。然而,某些窗口 消息直接通过win32k.sys发送(最值得注意的是 WM_CREATE)作为用户模式调用的直接同步结果

似乎对这个问题有所启发

对所有这些内核模式ex吞咽行为有一些很好的解释:

根本没有停下来 几年前,一个同样令人不安的问题被提出 64位Windows,它会导致一些崩溃被静默忽略

结构化异常。。。依赖于能够展开堆栈 (不调用或不调用析构函数)以便传输 catch/\uuu except块发生异常的位置执行

64位Windows的引入使这一点变得复杂。在64位Windows上 不可能跨内核边界展开堆栈。也就是说,如果。。。在回调中引发异常,该回调是 应该在内核边界的另一侧处理,然后 Windows无法处理此问题

这似乎有点深奥,也不太可能——编写内核回调 这似乎是一种罕见的活动,但实际上相当普遍。在里面 特别是,WindowProc是一个回调,它通常由 内核


还有一个额外的好处:请看。

WM\u CLOSE本身没有什么特别之处。通过调用
\u try
/
\u处理程序下的
WndProc
,您的
WndProc
回调中未处理的异常始终被静默忽略。这是唯一安全的实现,因为。默认情况下,WindoWProcedure调用destroy window来销毁窗口。这可能是一个原因,任何异常都会被重定向到“/dev/null”:并且@IInspectable,我不认为Raymond Chen在他的文章中说
WndProc
回调受到保护。实际上,看起来他说的正好相反:
由于这种总体思维,Win32代码不太担心从异常中恢复。如果发生异常,则意味着您的流程已经结束,并且没有必要尝试修复它
。此外,如果回调受到保护,则在
WM_命令
处理程序中,空指针取消引用也不会崩溃。@Brad如果您不想吞没异常,则需要为应用程序提供与Win7兼容的清单,并编译为64位。否则,“旧的”Win2k3行为将生效,这将吞噬异常。@拥有有效的Windows 7清单足以更改该行为。详细信息请参见。我应该说得更准确一些:默认情况下并不是所有的异常都会被吞没,只有那些
WndProc
是内核函数的用户模式回调的异常才会被吞没(例如,
WM\u CREATE
/
WM\u NCCREATE
是调用
CreateWindow
的结果).我认为Windows默默地接受异常是可怕和令人震惊的——Windows默默地忽略崩溃。Raymond Chen的文章指出了在没有所有人参与的情况下处理异常的危险,这使得Windows这样做更加奇怪。顺便说一句,我是这个答案中链接的随机ASCII文章的作者。@BruceDawson-我确实同意可怕的说法,但毕竟我经历过wrt。Windows崩溃处理(从XP开始),我不太确定我是否发现了太多令人震惊的事情。:-。。。感谢您提供的随机ASCII-这里总是有大量资源!