当异常从finallys抛出catch块没有被评估(Catch block is not being

2019-07-31 05:46发布

这个问题出现,因为在.NET 4.0曾任职代码在.NET 4.5中未处理的异常失败,因为尝试/ finallys的部分。 如果您想详细信息,请仔细阅读在微软连接 。 我用它作为基地在这个例子中,所以它可能是有益的参考。

代码

对于谁选择不阅读这背后的问题细节的人来说,这里是一个非常快看看那里发生这种情况的条件:

using(var ms = new MemoryStream(encryptedData))
using(var cryptoStream = new CryptoStream(encryptedData, decryptor, CryptoStreamMode.Read))
using(var sr = new StreamReader(cryptoStream))

这个问题是有异常从抛出Dispose CryptoStream的方法(因为它们是用一个语句中,这些异常发生从两个不同的finally块抛出)。 当cryptoStream.Dispose()是由被叫StreamReader ,该CryptographicException异常。 第二次cryptoStream.Dispose()被调用时,在其使用的语句,它抛出一个ArgumentNullException

下面的代码去除大部分从上面提供的链接不必要的代码,并使用语句解开进入试/ finallys清楚地表明,他们是在finally块被抛出。

using System;
using System.Security.Cryptography;
namespace Sandbox
{
    public class Program
    {
        public static void Main(string[] args)
        {
            try
            {
                try
                {
                    try
                    {
                        Console.WriteLine("Propagate, my children");
                    }
                    finally
                    {
                        // F1
                        Console.WriteLine("Throwing CryptographicExecption");
                        throw new CryptographicException();
                    }
                }
                finally
                {
                    // F2
                    Console.WriteLine("Throwing ArgumentException");
                    throw new ArgumentException();
                }
            }
            catch (ArgumentException)
            {
                // C1
                Console.WriteLine("Caught ArgumentException");
            }
            // Same behavior if this was in an enclosing try/catch
            catch (CryptographicException)
            {
                // C2
                Console.WriteLine("Caught CryptographicException");
            }

            Console.WriteLine("Made it out of the exception minefield");
        }
    }}

注:尝试/终于对应于从引用的代码语句中使用扩展。

输出:

    Propagate, my children
    Throwing CryptographicExecption
    Throwing ArgumentException
    Caught ArgumentException
    Press any key to continue . . .

它不会出现该CryptographicException是不断执行catch块。 然而,除去catch块会导致异常终止运行系统。

有一点了解更多信息

编辑:这是更新到规范的最新版本。 我碰巧抓住过MSDN的一个较老了措辞。 Lost于已更新terminated

潜水到C#规范,部分8.9.5和8.10讨论异常行为:

  • 当一个异常被抛出,包括从finally块内,控制在一个封闭try语句转移到第一catch子句。 直到一个合适的被发现这样下去了try语句。
  • 如果异常是最后块的执行期间抛出,和一个异常已经被传播, 该异常终止

“终止”使得它似乎是第一个异常将永远被第二抛出的异常被隐藏,但似乎是发生了什么不。

我敢肯定,问题是在这里的某个地方

在大多数情况下,人们很容易想象一下运行时正在做什么。 该代码执行到所述第一最后块( F1 ,其中将引发异常)。 作为异常传播时,所述第二异常是从所述第二抛出最后块( F2 )。

根据规范,在CryptographicException从抛出F1现在终止,并且运行时寻找的处理程序ArgumentException 。 运行时查找处理程序,并执行在用于捕捉块中的代码ArgumentExceptionC1 )。

这是它得到云里雾里:规范说,第一个异常将被终止。 然而,如果第二个catch块( C2 )从代码,除去的CryptographicException该假想丢失,是现在终止程序未处理的异常。 与C2存在,该代码将不会从一个未处理的异常终止,所以在表面上看来可以处理异常,但永远不会执行该块中的实际的异常处理代码。

问题

这些问题基本上是相同的,但重新措辞的特异性。

  1. 它是如何使CryptographicException会因终止于ArgumentException从封闭抛出finally块,去掉了异常catch (CryptographicException)块会导致异常未处理去和终止运行系统?

  2. 由于运行时似乎处理CryptographicExceptioncatch (CryptographicException)块存在,为什么不执行块内的代码?


额外信息编辑

我仍然在寻找到这样做的实际行为,而许多问题的答案已经在至少回答以上问题的部分特别有帮助。

另一个奇怪的行为,当您运行与该代码发生catch (CryptographicException)注释掉块,是.NET 4.5和.NET 3.5的区别。 .NET 4.5将引发CryptographicException和终止应用程序。 .NET 3.5,但是,似乎根据C#规范在异常表现得更。

Propagate, my children
Throwing CryptographicExecption

Unhandled Exception: System.Security.Cryptography.CryptographicException [...]
ram.cs:line 23
Throwing ArgumentException
Caught ArgumentException
Made it out of the exception minefield

在.NET 3.5中,我看到了我在读规范。 唯一的例外变为“丢失”,或者“终止”,因为这永远需要被抓到的唯一的事情就是ArgumentException 。 正因为如此,程序可以继续执行。 我只有.NET 4.5我的机器上,我不知道如果这种情况发生在.NET 4.0中?

Answer 1:

在.NET异常处理有3个不同的阶段:

  • 阶段1个踢齿轮只要throw语句执行。 在CLR去寻找一个catch块的范围是advertizes它愿意来处理异常。 在这个阶段,在C#中, 没有代码执行 。 技术上讲,它是可以执行的代码,但这种能力是不是在C#中曝光。

  • 阶段2点开始一次捕捉块位于和CLR知道在哪里重新开始执行。 然后,它可以可靠地确定块需要执行什么最后。 任何方法堆栈帧被展开为好。

  • 第3周阶段开始,一旦所有的finally块完成,栈展开到包含catch语句的方法。 指令指针设置为在catch块中的第一条语句。 如果该块不包含任何futher throw语句,继续执行在过去catch块中的语句为正常。

因此,在你的代码片段的核心需求是有范围的catch(CryptographicException)。 没有它,阶段1失败,CLR不知道该如何继续执行。 线程是死的,平时也终止取决于异常处理政策方案。 最终块都不会执行。

如果在阶段2中的最后块抛出异常,则正常的异常处理序列被立即中断。 原来的异常“丢失”,而不会继续第3阶段所以不能在你的程序中观察到。 异常处理开始回到1级,现在正在寻找新的异常,并在该范围finally块开始。



Answer 2:

如果异常finally块的执行过程中抛出,而一个异常已经被传播,该异常丢失

基本上,当你执行发生的事情:

  • CryptographicException在内部终于抛出。
  • 外范围的最终执行,并抛出ArgumentException 。 由于“CryptographicException”是“正在将会传播”在这个时间点,很失落。
  • 最终捕获发生, ArgumentException被捕获。

......这是没有意义的第一个异常简单地消失在乙醚,只是因为有来自不同抛出另一个异常finally块。

这正是发生了什么,根据你所引用的C#语言规范。 第一个例外( CryptographicException )有效地消失了-这是“丢失”。

您只能通过显式使用达到这个状态finally ,虽然如此,我相信假设是,您所提供的错误与这种期望或可能性记处理(如你正在使用try ,在这一点上,这意味着你已经接受你可能有一个例外)。

这基本上是详细解释了规范8.9.5 (文中8.10你报的是指这部分):

如果最后块抛出另一个异常,则当前异常的处理被终止。

第一个例外,你的情况ArgumentException ,基本“消失”。



Answer 3:

事实证明,我是不是疯了。 基于我对这个问题的答案,我觉得它好像我难以理解什么是如此明确的规范概括。 这真的不是在所有的难以把握。

事实是,该规范是有道理的,而行为是不是。 当您运行在一个较旧的运行时,它的行为完全不同的代码,这被认为是更是这样......或者至少出现

快速回顾一下

我所看到的,我的Win7 x64的机器上:

  • .NET v2.0-3.5 - WER对话框时CryptographicException异常。 击中后Close the program ,该程序继续,仿佛execption从未抛出。 该应用程序没有终止 。 这是一个希望从阅读规范的行为,并深受建筑师定义谁在.NET中实现的异常处理。

  • .NET v4.0-4.5 - 不显示WER对话框。 相反,出现一个窗口询问您是否要调试程序。 点击no导致程序立即终止。 没有finally块之后执行。

事实证明,几乎任何人谁尝试回答我的问题会得到同样的结果和我一样,所以这可以解释为什么没有人能回答我为什么运行时间是从一个例外,它吞噬终止的问题。

这是从来没有你期待什么挺

谁曾怀疑刚刚在实时调试

您可能已经注意到,在.NET 2运行的应用程序产生比.NET 4不同的错误对话框但是,如果你像我一样,你所期待的开​​发周期过程中的窗口,所以你没有认为它是什么。

该vsjitdebugger可执行文件被强行终止,而不是让它继续申请。 在2.0运行, dw20.exe没有这种行为,其实你看到的第一件事是,WER消息。

由于JIT调试终止应用程序,它使人们看起来 ,这是不符合的时候,其实,它说什么规范。

为了测试这个,我禁用了vsjitdebugger发动失败,通过改变注册表项HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\Auto从1到0果然,应用程序忽略异常并继续,就像.NET 2.0。


事实证明,有一种变通方法,但真的没有理由要解决此问题,因为您的应用程序终止。

  1. 当刚刚在实时调试窗口弹出,检查Manually choose the debugging engines ,并单击是,要调试。
  2. 当Visual Studio中为您提供发动机可供选择,单击取消。
  3. 这将导致程序继续运行,或WER对话框弹出,这取决于你的机器配置。 如果出现这种情况,告诉它关闭程序实际上不会关闭它,它会继续运行,就好像一切正​​常。


文章来源: Catch block is not being evaluated when exceptions are thrown from finallys