Exceções: alterações feitas em macros de exceção na versão 3.0

Esse é um tópico avançado.

No MFC versão 3.0 e posteriores, as macros de tratamento de exceção foram alteradas para usar exceções C++. Este artigo informa como essas alterações podem afetar o comportamento do código existente que usa as macros.

Este artigo aborda os seguintes tópicos:

Tipos de exceção e a macro CATCH

Em versões anteriores do MFC, a macro CATCH usava informações de tipo de tempo de execução do MFC para determinar o tipo de uma exceção. O tipo da exceção é determinado, em outras palavras, no site onde ela é capturada. Com exceções de C++, no entanto, o tipo da exceção sempre é determinado no site de lançamento pelo tipo do objeto de exceção que é gerado. Isso causará incompatibilidades no caso raro em que o tipo do ponteiro para o objeto gerado difere do tipo do objeto gerado.

O seguinte exemplo ilustra a consequência dessa diferença entre o MFC versão 3.0 e versões anteriores:

TRY
{
   THROW((CException*) new CCustomException());
}
CATCH(CCustomException, e)
{
   TRACE("MFC 2.x will land here\n");
}
AND_CATCH(CException, e)
{
   TRACE("MFC 3.0 will land here\n");
}
END_CATCH

Esse código se comporta de modo diferente na versão 3.0 porque o controle sempre passa para o primeiro bloco catch com uma declaração de exceção correspondente. O resultado da expressão de lançamento

THROW((CException*) new CCustomException());

é lançado como um CException*, mesmo que seja construído como um CCustomException. A macro CATCH nas versões do MFC 2.5 e anteriores usa CObject::IsKindOf para testar o tipo em tempo de execução. Como a expressão

e->IsKindOf(RUNTIME_CLASS(CException));

é verdadeira, o primeiro bloco catch captura a exceção. Na versão 3.0, que usa exceções de C++ para implementar muitas das macros de tratamento de exceções, o segundo bloco catch corresponde ao CException gerado.

Código como esse é incomum. Geralmente, ele aparece quando um objeto de exceção é passado para outra função que aceita um CException* genérico, executa o processamento "pré-lançamento" e, por fim, lança a exceção.

Para contornar esse problema, mova a expressão de lançamento da função para o código de chamada e gere uma exceção do tipo real conhecido pelo compilador no momento em que a exceção é gerada.

Lançando exceções novamente

Um bloco catch não pode lançar o mesmo ponteiro de exceção que capturou.

Por exemplo, esse código era válido em versões anteriores, mas terá resultados inesperados com a versão 3.0:

TRY
{
   // Do something to throw an exception.
   AfxThrowUserException();
}
CATCH(CException, e)
{
   THROW(e);    // Wrong. Use THROW_LAST() instead
}
END_CATCH
   }

Usar THROW no bloco catch faz com que o ponteiro e seja excluído, de modo que o site de captura externo recebe um ponteiro inválido. Use THROW_LAST para lançar e novamente.

Para obter mais informações, confira Exceções: como capturar e excluir exceções.

Confira também

Tratamento de exceção