Share via


Tratar exceções estruturadas no C++

A principal diferença entre o tratamento de exceções estruturado (SEH) C e o tratamento de exceções de C++ é que o modelo do tratamento de exceções de C++ lida com tipos, enquanto o modelo de tratamento de exceções estruturado do C lida exceções de um tipo — especificamente, unsigned int. Ou seja, as exceções C são identificadas por um valor inteiro não assinado, enquanto as exceções C++ são identificadas por tipo de dados. Quando uma exceção estruturada é gerada em C, cada manipulador possível executa um filtro que examina o contexto da exceção de C e determine se aceitará a exceção, a transmitirá para algum outro manipulador, ou a ignorará. Quando uma exceção é gerada em C++, ela pode ser de qualquer tipo.

Uma segunda diferença é que o modelo de tratamento de exceções estruturado de C é chamado de assíncrono, pois as exceções ocorrem secundariamente ao fluxo de controle normal. O mecanismo de tratamento de exceções de C++ é totalmente síncrono, o que significa que as exceções ocorrem apenas quando são geradas.

Quando você usa a opção do compilador /EHs ou /EHsc, nenhum manipulador de exceção do C++ lida com exceções estruturadas. Essas exceções são tratadas apenas por manipuladores de exceção estruturados __except ou manipuladores de terminação estruturados __finally. Para obter informações, confira Tratamento de exceções estruturadas (C/C++).

Na opção de compilador /EHa, se a exceção de C é gerada em um programa C/C++, pode ser tratada por um manipulador de exceções estruturado com o filtro associado ou por um manipulador de catch C++, seja qual estiver dinamicamente mais próximo ao contexto da exceção. Por exemplo, este programa C++ de exemplo gera uma exceção C dentro de um contexto C++ try:

Exemplo – capturar uma exceção C em um bloco catch do C++

// exceptions_Exception_Handling_Differences.cpp
// compile with: /EHa
#include <iostream>

using namespace std;
void SEHFunc( void );

int main() {
   try {
      SEHFunc();
   }
   catch( ... ) {
      cout << "Caught a C exception."<< endl;
   }
}

void SEHFunc() {
   __try {
      int x, y = 0;
      x = 5 / y;
   }
   __finally {
      cout << "In finally." << endl;
   }
}
In finally.
Caught a C exception.

Classes wrapper de exceção de C

Em um exemplo simples como o acima, a exceção de C pode ser capturada somente por um manipulador de reticências (...) catch. Nenhuma informação sobre o tipo ou a natureza de exceção é comunicada ao manipulador. Ainda que este método funcione, em alguns casos pode ser aconselhável definir uma transformação entre os dois modelos de tratamento de exceções para que cada exceção de C seja associada a uma classe específica. Para transformar uma, você pode definir a classe da exceção de C como "wrapper", que pode ser usada ou derivada de para atribuir um tipo específico da classe a uma exceção de C. Ao fazer isso, cada exceção C pode ser tratada separadamente por um manipulador C++ catch específico, em vez de todas em um único manipulador.

Sua classe wrapper pode ter uma interface que consiste em algumas funções de membro que determinam o valor de exceção, e que acessam informações estendidas de contexto de exceção fornecidas pelo modelo da exceção de C. Também pode ser aconselhável definir um construtor padrão e um construtor que aceite um argumento unsigned int (para fornecer para a representação subjacente da exceção de C) e um construtor de cópia bit a bit. Segue uma possível implementação da classe wrapper de exceção do C:

// exceptions_Exception_Handling_Differences2.cpp
// compile with: /c
class SE_Exception {
private:
   SE_Exception() {}
   SE_Exception( SE_Exception& ) {}
   unsigned int nSE;
public:
   SE_Exception( unsigned int n ) : nSE( n ) {}
   ~SE_Exception() {}
   unsigned int getSeNumber() {
      return nSE;
   }
};

Para usar essa classe, instale uma função personalizada de tradução da exceção de C, chamada pelo mecanismo interno de tratamento de exceções sempre que uma exceção de C é lançada. Dentro de sua função de tradução, você pode gerar qualquer exceção digitada (talvez um tipo SE_Exception, ou um tipo de classe derivada de SE_Exception) que possa ser capturada por um manipulador de correspondência C++ catch adequado. A função de tradução pode, em vez disso, retornar, o que indica que ela não conseguiu tratar a exceção. Se a própria função de tradução criar uma exceção de C, terminate é chamado.

Para especificar uma função personalizada de tradução, chame a função _set_se_translator com o nome da função de tradução como seu único argumento. A função de tradução que você grava é chamada uma vez para cada invocação de função na pilha que tem blocos try. Não há nenhuma função padrão de tradução; se você não especificar uma chamando _set_se_translator, a exceção de C só pode ser capturada por um manipulador de reticências catch.

Exemplo – Usar uma função de tradução personalizada

Por exemplo, o código a seguir instala uma função personalizada de tradução e gerará a exceção de C que é envolvida pela classe SE_Exception:

// exceptions_Exception_Handling_Differences3.cpp
// compile with: /EHa
#include <stdio.h>
#include <eh.h>
#include <windows.h>

class SE_Exception {
private:
   SE_Exception() {}
   unsigned int nSE;
public:
   SE_Exception( SE_Exception& e) : nSE(e.nSE) {}
   SE_Exception(unsigned int n) : nSE(n) {}
   ~SE_Exception() {}
   unsigned int getSeNumber() { return nSE; }
};

void SEFunc() {
    __try {
        int x, y = 0;
        x = 5 / y;
    }
    __finally {
        printf_s( "In finally\n" );
    }
}

void trans_func( unsigned int u, _EXCEPTION_POINTERS* pExp ) {
    printf_s( "In trans_func.\n" );
    throw SE_Exception( u );
}

int main() {
    _set_se_translator( trans_func );
    try {
        SEFunc();
    }
    catch( SE_Exception e ) {
        printf_s( "Caught a __try exception with SE_Exception.\n" );
        printf_s( "nSE = 0x%x\n", e.getSeNumber() );
    }
}
In trans_func.
In finally
Caught a __try exception with SE_Exception.
nSE = 0xc0000094

Confira também

Misturando exceções C (estruturadas) e C++