O bloco de captura não está sendo avaliado quando exceções são lançadas de finalmente

Essa questão surgiu porque o código que funcionava anteriormente no .NET 4.0 falhou com uma exceção não tratada no .NET 4.5, parcialmente devido a try / finallys. Se você quiser detalhes, leia mais emConexão da Microsoft. Eu usei como base para este exemplo, então pode ser útil fazer referência.

O código

Para as pessoas que escolheram não ler sobre os detalhes por trás dessa questão, aqui está uma rápida olhada nas condições em que isso aconteceu:

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

Esta questão é que existem exceções lançadas a partir doDispose método de CryptoStream (já que eles estão dentro de uma instrução using, estas exceções foram lançadas de dois blocos finally diferentes). QuandocryptoStream.Dispose() é chamado peloStreamReader, aCryptographicException é lançado. A segunda vezcryptoStream.Dispose() é chamado, em sua declaração de uso, lança umArgumentNullException

O código a seguir remove a maior parte do código desnecessário do link fornecido acima e desenrola as instruções using em try / finallys para mostrar claramente que elas estão sendo lançadas em blocos finais.

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: O try / finally corresponde ao expandido usando instruções do código referenciado.

Saída:

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

Não parece que oCryptographicException bloco de captura é sempre executado. No entanto, a remoção desse bloco catch faz com que a exceção finalize o tempo de execução.

Um pouco mais de informação

EDIT: isto foi atualizado para a revisão mais recente da especificação. Aquela que eu peguei do MSDN tinha um texto mais antigo.Lost foi atualizado paraterminated.

Mergulhando na especificação C #, as seções 8.9.5 e 8.10 discutem o comportamento de exceção:

Quando uma exceção é lançada, incluindo de dentro de um bloco finally, o controle é transferido para a primeira cláusula catch em uma instrução try envolvente. Isso continua até tentar instruções até encontrar uma adequada.Se uma exceção é lançada durante a execução de um bloco finally, e uma exceção já estava sendo propagada,essa exceção é terminada

"Terminado" faz parecer que a primeira exceção seria para sempre escondida pela segunda exceção, embora não pareça ser o que está acontecendo.

Tenho certeza que a questão está aqui em algum lugar

Na maior parte, é fácil visualizar o que o tempo de execução está fazendo. O código é executado no primeiro bloco final (F1) onde uma exceção é lançada. À medida que a exceção se propaga, a segunda exceção é lançada do segundo bloco finally (F2).

De acordo com as especificações, oCryptographicException jogado deF1 agora está terminado, e o tempo de execução está procurando por um manipulador para oArgumentException. O tempo de execução localiza um manipulador e executa o código no bloco catch para oArgumentException (C1).

Aqui é onde fica nebuloso: a especificação diz que a primeira exceção seria encerrada. No entanto, se o segundo bloco catch (C2) é removido do código, oCryptographicException que foi supostamente perdido, é agora uma exceção não tratada que encerra o programa. Com oC2 presente, o código não terminará de uma exceção não tratada, portanto, na superfícieaparece estar lidando com a exceção, mas o código de manipulação de exceção no bloco nunca é executado.

Questões

As perguntas são basicamente as mesmas, mas reformuladas para especificidade.

Como é que oCryptographicException torna-se terminado devido àArgumentException exceção lançada do bloco finally final, removendo ocatch (CryptographicException) block faz com que a exceção não seja tratada e encerre o tempo de execução?

Como o tempo de execução parece estar lidando comCryptographicException quando ocatch (CryptographicException) bloco está presente, por que o código dentro do bloco não está sendo executado?

Extra informacional Editar

Ainda estou olhando para o comportamento real disso, e muitas das respostas foram particularmente úteis para, pelo menos, responder a partes das perguntas acima.

Outro comportamento curioso, que acontece quando você executa o código com ocatch (CryptographicException) block commented out, é a diferença entre o .NET 4.5 e o .NET 3.5. O .NET 4.5 lançará oCryptographicException e encerrar o aplicativo. .NET 3.5, no entanto, parece se comportar mais de acordo com a especificação C # onde a exceção.

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

No .NET 3.5, vejo o que li na especificação. A exceção se torna "perdida" ou "terminada", já que a única coisa que precisa ser pego é aArgumentException. Por causa disso, o programa pode continuar a execução. Eu só tenho o .NET 4.5 na minha máquina, gostaria de saber se isso acontece no .NET 4.0?

questionAnswers(3)

yourAnswerToTheQuestion