Concepts de base dans l'utilisation des exceptions managées
Cette rubrique décrit la gestion des exceptions dans les applications managées. Autrement dit, une application compilée avec l’option du compilateur /clr .
Dans cette rubrique
Notes
Si vous compilez avec l’option /clr , vous pouvez gérer les exceptions CLR ainsi que la classe standard Exception fournit de nombreuses méthodes utiles pour traiter les exceptions CLR et est recommandée comme classe de base pour les classes d’exception définies par l’utilisateur.
L’interception des types d’exceptions dérivés d’une interface n’est pas prise en charge sous /clr. En outre, le Common Language Runtime ne vous permet pas d’intercepter les exceptions de dépassement de capacité de pile ; une exception de dépassement de capacité de pile met fin au processus.
Pour plus d’informations sur les différences de gestion des exceptions dans les applications managées et non managées, consultez Différences dans le comportement de gestion des exceptions sous Extensions managées pour C++.
Levée d’exceptions sous /clr
L’expression de levée C++ est étendue pour lever un handle vers un type CLR. L’exemple suivant crée un type d’exception personnalisé, puis lève une instance de ce type :
// clr_exception_handling.cpp
// compile with: /clr /c
ref struct MyStruct: public System::Exception {
public:
int i;
};
void GlobalFunction() {
MyStruct^ pMyStruct = gcnew MyStruct;
throw pMyStruct;
}
Un type valeur doit être boxé avant d’être levée :
// clr_exception_handling_2.cpp
// compile with: /clr /c
value struct MyValueStruct {
int i;
};
void GlobalFunction() {
MyValueStruct v = {11};
throw (MyValueStruct ^)v;
}
Try/Catch Blocks for CLR Extensions
La même try
/catch
structure de bloc peut être utilisée pour intercepter les exceptions CLR et natives :
// clr_exception_handling_3.cpp
// compile with: /clr
using namespace System;
ref struct MyStruct : public Exception {
public:
int i;
};
struct CMyClass {
public:
double d;
};
void GlobalFunction() {
MyStruct^ pMyStruct = gcnew MyStruct;
pMyStruct->i = 11;
throw pMyStruct;
}
void GlobalFunction2() {
CMyClass c = {2.0};
throw c;
}
int main() {
for ( int i = 1; i >= 0; --i ) {
try {
if ( i == 1 )
GlobalFunction2();
if ( i == 0 )
GlobalFunction();
}
catch ( CMyClass& catchC ) {
Console::WriteLine( "In 'catch(CMyClass& catchC)'" );
Console::WriteLine( catchC.d );
}
catch ( MyStruct^ catchException ) {
Console::WriteLine( "In 'catch(MyStruct^ catchException)'" );
Console::WriteLine( catchException->i );
}
}
}
Sortie
In 'catch(CMyClass& catchC)'
2
In 'catch(MyStruct^ catchException)'
11
Ordre de déroulement pour les objets C++
Le déroulement se produit pour tous les objets C++ avec des destructeurs qui peuvent se trouver sur la pile d’exécution entre la fonction de levée et la fonction de gestion. Étant donné que les types CLR sont alloués sur le tas, le déroulement ne s’applique pas à eux.
L’ordre des événements pour une exception levée est le suivant :
Le runtime guide la pile à la recherche de la clause catch appropriée, ou dans le cas de SEH, un filtre à l’exception de SEH, pour intercepter l’exception. Les clauses Catch sont recherchées en premier dans l’ordre lexical, puis dans la pile des appels de manière dynamique.
Une fois le gestionnaire correct trouvé, la pile est déwound à ce point. Pour chaque appel de fonction sur la pile, ses objets locaux sont destructeurs et __finally blocs sont exécutés, à partir de la plupart des éléments imbriqués vers l’extérieur.
Une fois la pile déwound, la clause catch est exécutée.
Intercepter des types non managés
Lorsqu’un type d’objet non managé est levée, il est encapsulé avec une exception de type SEHException. Lorsque vous recherchez la clause appropriée catch
, il existe deux possibilités.
Si un type C++ natif est rencontré, l’exception est annulée et comparée au type rencontré. Cette comparaison permet à un type C++ natif d’être intercepté de la façon normale.
Toutefois, si une
catch
clause de type SEHException ou une de ses classes de base est examinée en premier, la clause intercepte l’exception. Par conséquent, vous devez placer toutes les clauses catch qui interceptent d’abord les types C++ natifs avant les clauses catch des types CLR.
Notez les points suivants :
catch(Object^)
et
catch(...)
intercepte tous les deux les types levées, y compris les exceptions SEH.
Si un type non managé est intercepté par catch(Object^), il ne détruit pas l’objet levée.
Lorsque vous lèvez ou interceptez des exceptions non managées, nous vous recommandons d’utiliser l’option du compilateur /EHsc au lieu de /EHs ou /EHa.