jqu*_*uth 18 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();
}
}
Run Code Online (Sandbox Code Playgroud)
现在,当我将其编译为目标3.5(2.0 CLR)时,它将弹出一个窗口,说"XXX已停止工作".如果我现在单击取消按钮它将运行finally,如果我等到它完成查找并单击关闭程序按钮它也将运行finally.
现在有趣和令人困惑的是,如果我针对4.0编译同样的事情点击取消按钮将运行finally块并单击关闭程序按钮不会.
我的问题是:为什么在点击关闭程序按钮时,终于在2.0上运行而不在4.0上运行?这有什么影响?
编辑:我在Windows 7 32位的发布模式(内置发布模式)中从命令提示符运行此命令.错误消息:在Windows查找问题后,下面的第一个结果是3.5点击关闭,第二个是我在4.0上运行它并执行相同的操作.
Dir*_*mar 20
我现在能够重现这种行为(当我第一次阅读时,我没有从你的问题中得到确切的步骤).
我可以观察到的一个区别是.NET运行时处理未处理的异常的方式.CLR 2.0运行一个名为Microsoft .NET Error Reporting 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的信息项记录到事件日志中,并且不会终止该过程.
Eri*_*ert 10
想想这种情况有多糟糕:发生了意想不到的事情,没有人编写代码来处理.在这种情况下做正确的事情上运行更多的代码,那可能还没有建立处理这种情况?可能不是.通常,在这里做的正确的事情是不要尝试运行finally块,因为这样做会使情况更糟.你已经知道这个过程正在下降; 立刻摆脱痛苦.
在未处理的异常将取消该过程的情况下,任何事情都可能发生.它是实现定义的,在这种情况下会发生什么:是否向Windows错误报告报告错误,是否启动调试器,等等.CLR完全有权尝试运行finally块,并且完全在其快速失败的权利范围内.在这种情况下,所有赌注都已关闭; 不同的实现可以选择做不同的事情.
关于这个主题的所有知识都来自这篇文章: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">
Run Code Online (Sandbox Code Playgroud)
原因是:当在.NET中引发异常时,它开始向后走回堆栈,寻找异常处理程序,当它找到一个异常处理程序时,然后在运行catch的内容之前再次遍历运行finally块的堆栈.如果它没有找到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块可以运行(文章没有进入这个位.)
我相信这与调试器连接方式的更改有关。
当调试器无法启动或没有应启动的已注册调试器时,您不再收到通知。
发生的情况是您选择启动调试器,但您取消了它。我相信这属于这一类别,并且应用程序因此而停止。
归档时间: |
|
查看次数: |
2958 次 |
最近记录: |