El bloque de captura no se está evaluando cuando se lanzan excepciones de finallys

Esta pregunta surgió porque el código que funcionaba anteriormente en .NET 4.0 falló con una excepción no controlada en .NET 4.5, en parte debido a try / finallys. Si quieres detalles, lee más enMicrosoft se conecta. Lo utilicé como base para este ejemplo, por lo que podría ser útil hacer referencia.

El código

Para las personas que eligieron no leer sobre los detalles detrás de esta pregunta, aquí hay un vistazo muy rápido a las condiciones en las que sucedió esto:

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

Este problema es que hay excepciones lanzadas desde elDispose Método de CryptoStream (ya que están dentro de una declaración de uso, estas excepciones son lanzadas desde dos bloques finalmente diferentes). CuandocryptoStream.Dispose() es llamado por elStreamReader, laCryptographicException es aventado. La segunda vezcryptoStream.Dispose() Se llama, en su declaración de uso, lanza unArgumentNullException

El siguiente código elimina la mayoría del código innecesario del enlace proporcionado anteriormente, y desenrolla las declaraciones de uso en try / finallys para mostrar claramente que se están lanzando finalmente en bloques.

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");
        }
    }}

Nota: El intento / finalmente corresponde a las declaraciones expandidas usando el código al que se hace referencia.

Salida:

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

No parece que elCryptographicException bloque de captura se ejecuta siempre. Sin embargo, la eliminación de ese bloque catch hace que la excepción finalice el tiempo de ejecución.

Un poco más de información.

EDITAR: Esto se actualizó a la revisión más reciente de la especificación. El que se me ocurrió sacar de MSDN tenía una redacción más antigua.Lost ha sido actualizado aterminated.

Al sumergirse en la especificación de C #, las secciones 8.9.5 y 8.10 discuten el comportamiento de las excepciones:

Cuando se lanza una excepción, incluso desde dentro de un bloque finally, el control se transfiere a la primera cláusula catch en una declaración de intento adjunta. Esto continúa hasta las declaraciones de prueba hasta que se encuentra una adecuada.Si se lanza una excepción durante la ejecución de un bloque finally y ya se está propagando una excepción,esa excepción se termina

"Terminado" hace que parezca que la primera excepción quedaría siempre oculta por la segunda excepción lanzada, aunque no parece ser lo que está sucediendo.

Estoy seguro de que la pregunta está aquí en alguna parte

En su mayor parte, es fácil visualizar lo que está haciendo el tiempo de ejecución. El código se ejecuta hasta el primer bloque finalmente (F1) donde se lanza una excepción. A medida que la excepción se propaga, la segunda excepción se lanza desde el segundo bloque finally (F2).

Según la especificación, elCryptographicException arrojado desdeF1 ahora está terminado, y el tiempo de ejecución está buscando un controlador para elArgumentException. El tiempo de ejecución encuentra un controlador y ejecuta el código en el bloque catch para elArgumentException (C1).

Aquí es donde se pone borroso: la especificación dice que la primera excepción se terminará. Sin embargo, si el segundo bloque catch (C2) se elimina del código, elCryptographicException que supuestamente se perdió, ahora es una excepción no controlada que finaliza el programa. Con elC2 presente, el código no terminará a partir de una excepción no controlada, por lo que en la superficie seaparece para manejar la excepción, pero el código de manejo de excepción en realidad en el bloque nunca se ejecuta.

Preguntas

Las preguntas son básicamente las mismas, pero rediseñadas para la especificidad.

Como es que elCryptographicException se termina debido a laArgumentException excepción lanzada desde el bloque que finalmente lo encierra, como quitar elcatch (CryptographicException) ¿El bloque hace que la excepción no sea manejada y termine el tiempo de ejecución?

Dado que el tiempo de ejecución parece estar manejando elCryptographicException cuando elcatch (CryptographicException) el bloque está presente, ¿por qué el código dentro del bloque no se está ejecutando?

Información adicional Editar

Todavía estoy investigando el comportamiento real de esto, y muchas de las respuestas han sido particularmente útiles al responder, al menos, partes de las preguntas anteriores.

Otro comportamiento curioso, que ocurre cuando ejecutas el código con elcatch (CryptographicException) bloque comentado, es la diferencia entre .NET 4.5 y .NET 3.5. .NET 4.5 lanzará elCryptographicException y terminar la aplicación. .NET 3.5, sin embargo, parece comportarse más de acuerdo con la especificación de C # donde se encuentra la excepción.

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

En .NET 3.5, veo lo que leo en la especificación. La excepción se "pierde" o "termina", ya que lo único que necesita ser atrapado es elArgumentException. Por eso el programa puede continuar ejecutándose. Sólo tengo .NET 4.5 en mi máquina, me pregunto si esto sucede en .NET 4.0.

Respuestas a la pregunta(3)

Su respuesta a la pregunta