当从finally中抛出异常时,Catch块不会被评估

2024-06-20

出现这个问题的原因是之前在 .NET 4.0 中运行的代码在 .NET 4.5 中因未处理的异常而失败,部分原因是 try/finallys。如果您想了解详细信息,请阅读更多内容微软连接 https://connect.microsoft.com/VisualStudio/feedback/details/759833/cryptostream-behaves-differently-in-net-4-5-breaks-existing-net-4-0-code。我用它作为本示例的基础,因此可能对参考有所帮助。

The code

对于选择不阅读此问题背后的详细信息的人,以下是发生这种情况的条件的快速浏览:

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

这个问题是有异常抛出DisposeCryptoStream 的方法(因为它们位于 using 语句内,所以这些异常恰好是从两个不同的 finally 块抛出的)。什么时候cryptoStream.Dispose()被称为StreamReader, the CryptographicException被抛出。第二次cryptoStream.Dispose()被调用,在其 using 语句中,它抛出一个ArgumentNullException

以下代码从上面提供的链接中删除了大部分不必要的代码,并将 using 语句展开到 try/finally 中,以清楚地表明它们被抛出到 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");
        }
    }}

注意:try/finally 对应于引用代码中的扩展 using 语句。

Output:



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

看来并没有CryptographicExceptioncatch 块曾经被执行过。但是,删除该 catch 块会导致异常终止运行时。

更多信息

编辑:这已更新为规范的最新版本。我碰巧从 MSDN 上找到的一份措辞较旧。Lost已更新为terminated.

深入研究 C# 规范,第 8.9.5 节和第 8.10 节讨论了异常行为:

  • 当抛出异常时,包括从 finally 块内部抛出异常时,控制权将转移到封闭的 try 语句中的第一个 catch 子句。这将继续尝试语句,直到找到合适的语句为止。
  • 如果在执行finally块期间抛出异常,并且异常已经被传播,该异常已终止

“终止”使得第一个异常看起来将永远被第二个抛出的异常隐藏,尽管它似乎并不是正在发生的事情。

我确信问题就在这里某个地方

在大多数情况下,很容易可视化运行时正在做什么。代码执行到第一个finally块(F1) 抛出异常的地方。当异常传播时,第二个异常从第二个finally块中抛出(F2).

根据规范,CryptographicException抛出自F1现在已终止,运行时正在寻找处理程序ArgumentException。运行时找到一个处理程序,并执行 catch 块中的代码ArgumentException (C1).

这就是它变得模糊的地方:规范说第一个异常将被终止。但是,如果第二个 catch 块 (C2) 从代码中删除,CryptographicException本来应该丢失的,现在是一个未处理的异常,会终止程序。随着C2目前,代码不会因未处理的异常而终止,因此从表面上看appears处理异常,但块中实际的异常处理代码从未被执行。

问题

问题基本相同,但为了特殊性而重新措辞。

  1. 怎么会是CryptographicException由于以下原因而终止ArgumentException从封闭的finally块中抛出异常,如删除catch (CryptographicException)块会导致异常未处理并终止运行时吗?

  2. 由于运行时似乎正在处理CryptographicException当。。。的时候catch (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中是否会发生这种情况?


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

  • 一旦执行了 throw 语句,第 1 阶段就会启动。 CLR 会寻找范围内的 catch 块,以表明它愿意处理异常。在这个阶段,在 C# 中,没有代码执行。从技术上讲,执行代码是可能的,但 C# 中并未公开该功能。

  • 一旦找到 catch 块并且 CLR 知道从哪里恢复执行,阶段 2 就会开始。然后它可以可靠地确定需要执行哪些finally 块。任何方法堆栈帧也会被展开。

  • 一旦所有finally 块完成并且堆栈展开到包含catch 语句的方法,阶段3 就会开始。指令指针设置为 catch 块中的第一条语句。如果该块不再包含 throw 语句,则执行会在 catch 块之后的语句处正常恢复。

因此,代码片段中的一个核心要求是范围内有一个 catch (CryptographicException)。如果没有它,第 1 阶段就会失败,并且 CLR 不知道如何恢复执行。线程已死亡,通常还会根据异常处理策略终止程序。任何finally 块都不会执行。

如果在第 2 阶段,finally 块抛出异常,则正常的异常处理序列将立即中断。最初的异常是“丢失”的,它永远不会进入第 3 阶段,因此无法在您的程序中观察到。异常处理从第 1 阶段开始,现在寻找新的异常并从 finally 块的范围开始。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当从finally中抛出异常时,Catch块不会被评估 的相关文章

  • Unix网络编程澄清

    我正在翻阅这本经典书籍Unix网络编程 https rads stackoverflow com amzn click com 0139498761 当我偶然发现这个程序时 第 6 8 节 第 179 180 页 include unp h
  • 如何检查QProcess是否正确执行?

    QProcess process sdcompare QString command sdcompare QStringList args sdcompare command sdcompare diff args sdcompare lt
  • 如何将隐藏字段从一个页面传递到另一页面?

    我有一个 Net 类库 我想从一次代码隐藏页面传递一个隐藏变量 并在另一个代码隐藏页面中获取它 请注意 我没有任何可以使用表单标签和 get post 方法的设计页面 aspx 页面 我们应该怎么做 注意 我想使用隐藏字段将值从一页传递到另
  • 如何在 C# 中从 UNIX 纪元时间转换并考虑夏令时?

    我有一个从 unix 纪元时间转换为 NET DateTime 值的函数 public static DateTime FromUnixEpochTime double unixTime DateTime d new DateTime 19
  • 单元测试一起运行时失败,单独运行时通过

    所以我的单元测试遇到了一些问题 我不能只是将它们复制并粘贴到这里 但我会尽力而为 问题似乎是 如果我一项一项地运行测试 一切都会按预期进行 但如果我告诉它一起运行测试 则 1 5 将通过 TestMethod public void Obj
  • C++中的类查找结构体数组

    我正在尝试创建一个结构数组 它将输入字符串链接到类 如下所示 struct string command CommandPath cPath cPathLookup set an alarm AlarmCommandPath send an
  • 将 System.Windows.Input.KeyEventArgs 键转换为 char

    我需要将事件参数作为char 但是当我尝试转换 Key 枚举时 我得到的字母和符号与传入的字母和符号完全不同 如何正确地将密钥转换为字符 这是我尝试过的 ObserveKeyStroke this new ObervableKeyStrok
  • C# Dns.GetHostEntry 不返回连接到 WiFi 的移动设备的名称

    我有一个 C 中的 Windows 窗体应用程序 我试图获取列表中所有客户端的主机名 下面给出的是 ra00l 来自此链接的代码示例 GetHostEntry 非常慢 https stackoverflow com questions 99
  • 获取 WPF 控件的所有附加事件处理程序

    我正在开发一个应用程序 在其中动态分配按钮的事件 现在的问题是 我希望获取按钮单击事件的所有事件 因为我希望删除以前的处理程序 我尝试将事件处理程序设置为 null 如下所示 Button Click null 但是我收到了一个无法分配 n
  • 如何在 Linq 中获得左外连接?

    我的数据库中有两个表 如下所示 顾客 C ID city 1 Dhaka 2 New york 3 London 个人信息 P ID C ID Field value 1 1 First Name Nasir 2 1 Last Name U
  • 单击 form2 上的按钮触发 form 1 中的方法

    我对 Windows 窗体很陌生 我想知道是否可以通过单击表单 2 中的按钮来触发表单 1 中的方法 我的表格 1 有一个组合框 我的 Form 2 有一个 保存 按钮 我想要实现的是 当用户单击表单 2 中的 保存 时 我需要检查表单 1
  • 使用 JNI 从 Java 代码中检索 String 值的内存泄漏

    我使用 GetStringUTFChars 从使用 JNI 的 java 代码中检索字符串的值 并使用 ReleaseStringUTFChars 释放该字符串 当代码在 JRE 1 4 上运行时 不会出现内存泄漏 但如果相同的代码在 JR
  • 使用 Moq 使用内部构造函数模拟类型

    我正在尝试模拟 Microsoft Sync Framework 中的一个类 它只有一个内部构造函数 当我尝试以下操作时 var fullEnumerationContextMock new Mock
  • 如何对 Web Api 操作进行后调用?

    我创建了一个 Web API 操作 如下所示 HttpPost public void Load string siteName string providerName UserDetails userDetails implementat
  • .NET中的LinkedList是循环链表吗?

    我需要一个循环链表 所以我想知道是否LinkedList是循环链表吗 每当您想要移动列表中的 下一个 块时 以循环方式使用它的快速解决方案 current current Next current List First 电流在哪里Linke
  • (de)从 CSV 序列化为对象(或者最好是类型对象的列表)

    我是一名 C 程序员 试图学习 C 似乎有一些内置的对象序列化 但我在这里有点不知所措 我被要求将测试数据从 CSV 文件加载到对象集合中 CSV 比 xml 更受青睐 因为它更简单且更易于人类阅读 我们正在创建测试数据来运行单元测试 该集
  • C++ 密码屏蔽

    我正在编写一个代码来接收密码输入 下面是我的代码 程序运行良好 但问题是除了数字和字母字符之外的其他键也被读取 例如删除 插入等 我知道如何避免它吗 特q string pw char c while c 13 Loop until Ent
  • 编译时“strlen()”有效吗?

    有时需要将字符串的长度与常量进行比较 例如 if line length gt 2 Do something 但我试图避免在代码中使用 魔法 常量 通常我使用这样的代码 if line length gt strlen Do somethi
  • 线程和 fork()。我该如何处理呢? [复制]

    这个问题在这里已经有答案了 可能的重复 多线程程序中的fork https stackoverflow com questions 1235516 fork in multi threaded program 如果我有一个使用 fork 的
  • memset 未填充数组

    u32 iterations 5 u32 ecx u32 malloc sizeof u32 iterations memset ecx 0xBAADF00D sizeof u32 iterations printf 8X n ecx 0

随机推荐