出现这个问题的原因是之前在 .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))
这个问题是有异常抛出Dispose
CryptoStream 的方法(因为它们位于 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 . . .
看来并没有CryptographicException
catch 块曾经被执行过。但是,删除该 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处理异常,但块中实际的异常处理代码从未被执行。
问题
问题基本相同,但为了特殊性而重新措辞。
-
怎么会是CryptographicException
由于以下原因而终止ArgumentException
从封闭的finally块中抛出异常,如删除catch (CryptographicException)
块会导致异常未处理并终止运行时吗?
-
由于运行时似乎正在处理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中是否会发生这种情况?