最后Block没有运行?

好吧这是一个奇怪的问题,我希望有人可以解释一下。 我有以下代码:

static void Main(string[] args) { try { Console.WriteLine("in try"); throw new EncoderFallbackException(); } catch (Exception) { Console.WriteLine("in Catch"); throw new AbandonedMutexException(); } finally { Console.WriteLine("in Finally"); Console.ReadLine(); } } 

现在,当我将其编译为目标3.5(2.0 CLR)时,它将弹出一个窗口,说“XXX已停止工作”。 如果我现在单击取消按钮它将运行finally,如果我等到它完成查找并单击关闭程序按钮它也将运行finally。

现在有趣和令人困惑的是,如果我对4.0编译同样的事情点击取消按钮将运行finally块并单击关闭程序按钮不会。

我的问题是:为什么在点击关闭程序按钮时,终于在2.0而不是4.0上运行? 这有什么影响?

编辑:我在Windows 7 32位的发布模式(内置发布模式)中从命令提示符运行此命令。 错误消息:在Windows查找问题后,下面的第一个结果是3.5点击关闭,第二个是我在4.0上运行它并执行相同的操作。

替代文字

我现在能够重现这种行为(当我第一次阅读时,我没有从你的问题中得到确切的步骤)。

我可以观察到的一个区别是.NET运行时处理未处理的exception的方式。 CLR 2.0运行一个名为Microsoft .NET Error Reporting Shimdw20.exe )的帮助程序,而CLR 4.0启动Windows错误报告WerFault.exe )。

我假设两者在终止崩溃过程方面有不同的行为。 WerFault.exe显然立即杀死了.NET进程,而.NET错误报告Shim以某种方式关闭了应用程序,以便仍然执行finally块。

另请查看事件查看器:WerFault记录应用程序错误,通知崩溃的进程已终止:

应用程序:ConsoleApplication1.exe
框架版本:v4.0.30319
描述:由于未处理的exception,进程终止。
exception信息:System.Threading.AbandonedMutexException
堆:
   在Program.Main(System.String [])

但是,dw20.exe仅将事件ID为1001的信息项记录到事件日志中,并且不会终止该过程。

想想这种情况有多糟糕:发生了一些意想不到的事情,没有人编写代码来处理。 在这种情况下运行更多代码是正确的,这可能也不是为了处理这种情况而构建的吗? 可能不是。 通常,在这里做的正确的事情是不要尝试运行finally块,因为这样做会使情况更糟。 你已经知道这个过程正在发生变化; 立刻摆脱痛苦。

在未处理的exception将要取消该过程的情况下, 任何事情都可能发生。 它是实现定义在这种情况下发生的事情:是否向Windows错误报告报告错误,是否启动调试器,等等。 CLR完全有权尝试运行finally块,并且完全在其快速失败的权利范围内。 在这种情况下,所有赌注都已关闭; 不同的实现可以选择做不同的事情。

我对这个主题的所有知识都来自这篇文章: http : //msdn.microsoft.com/en-us/magazine/cc793966.aspx – 请注意它是为.NET 2.0编写的,但我觉得它很有意义对于我们在这种情况下遇到的情况(超过“因为它决定”反正)

快“我没有时间阅读那篇文章”答案(虽然你应该,这是一个非常好的):

这个问题的解决方案(如果你绝对必须运行你的finally块)将是a)放入一个全局error handling程序或b)强制.NET总是运行finally块并按照它做的方式做事(可以说是错误的在.NET 1.1中 – 将以下内容放在app.config中:

  

原因是:当在.NET中引发exception时,它开始向后走回堆栈,寻找exception处理程序,当它找到一个exception处理程序时,然后运行catch的内容之前再次遍历运行finally块的堆栈。 如果它没有找到catch,那么第二次遍历永远不会发生,因此finally块永远不会在这里运行,这就是为什么全局exception处理程序将始终运行finally子句,因为CLR将在找到catch时运行它们,而不是在它运行时(我相信这意味着即使你做了一个捕获/抛出你的终止块仍然会运行)。

app.config修复工作的原因是因为对于.NET 1.0和1.1,CLR中有一个全局捕获,它会在它们不受管理之前吞下exception,这当然会触发finally块运行。 当然,框架无法充分了解所述Exception来处理它,例如堆栈溢出,所以这可能是错误的做法。

下一点是它有点粘,我根据文章在这里说的做出假设。

如果您使用的是.NET 2.0+而没有遗留的exception处理,那么您的exception将会出现在Windowsexception处理系统(SEH)中,它似乎与CLR相似,因为它会向后遍历帧,直到它无法执行找到一个catch然后调用一系列称为Unhandled Exception Filter(UEF)的事件。 这是一个你可以订阅的事件,但它一次只能有一个订阅它的东西,所以当有东西订阅Windows时它会把它之前的回调地址交给你,允许你建立一个UEF链处理程序 – 但他们没有必要尊重该地址,他们应该自己调用地址,但是如果一个人破坏了链接,那么你就不会再处理错误了。 我假设这是取消Windows错误报告时发生的情况,它会破坏UEF链,这意味着应用程序立即关闭,并且finally块不会运行,但是如果让它运行到最后并关闭它,它会调用链中的下一个UEF。 .NET将注册一个调用AppDomain.UnhandledException的内容(因此即使这个事件也不能得到保证),我认为这也是你从中调用finally块的地方 – 因为我看不到如果你永远不会过渡回到CLR,一个托管的finally块可以运行(文章没有进入这个位。)

我相信这与更改调试器的附加方式有关。

从.NET Framework 4迁移问题文档:

当调试器无法启动时,或者没有应该启动的已注册调试器时,您将不再收到通知。

会发生什么是您选择启动调试器,但是您取消它。 我相信这属于这一类,应用程序因此而停止。

在发布和调试中都是这样,在框架3.5和4.0中,我在所有实例中看到“在最后”,是从命令行运行它,直到关闭我的vs会话,也许它在你的机器上或作为Kobi指出,也许平台相关(我在Win7 x64上)