最后Block没有运行?

时间:2010-11-16 11:07:53

标签: c# exception-handling c#-4.0

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

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上运行它并执行相同的操作。

alt text

5 个答案:

答案 0 :(得分:20)

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

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

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

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

Application: ConsoleApplication1.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.Threading.AbandonedMutexException
Stack:
   at Program.Main(System.String[])

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

答案 1 :(得分:10)

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

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

答案 2 :(得分:6)

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

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

问题的解决方案(如果绝对必须运行你的finally块)将是a)放入一个全局错误处理程序或b)强制.NET始终运行finally块并按照它的方式执行操作(在.NET 1.1中可以说是错误的方法 - 将以下内容放在app.config中:

<legacyUnhandledExceptionPolicy enabled="1">

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

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

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

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

答案 3 :(得分:2)

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

来自.NET Framework 4 Migration Issues文件:

  

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

您会选择启动调试器,但取消它。我认为这属于这一类,应用程序因此而停止。

答案 4 :(得分:0)

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