Partager via


Gestion structurée des exceptions (C/C++)

Bien que Windows et Visual C++ prennent en charge la gestion structurée des exceptions (GSE), nous vous recommandons d'utiliser la gestion des exceptions de l'ISO- norme C++ car elle rend le code davantage portable et flexible. Néanmoins vous devrez peut-être encore utiliser la GSE pour le code existant ou pour des types particuliers de programmes.

Grammaire

try-except-statement :

__try compound-statement

__except ( expression ) compound-statement

Notes

Avec la GSE, vous pouvez garantir que les ressources telles que les blocs de mémoire et les fichiers seront correctes si l'exécution se termine de façon inattendue. Vous pouvez également gérer des problèmes spécifiques - par exemple, une mémoire insuffisante - en utilisant un code structuré concis qui ne repose pas sur les instructions goto ou sur des tests élaborés de codes de retour.

Les instructions si-sauf et si-sinon référencées dans cet article sont des extensions Microsoft du langage C. Elles supportent la GSE en permettant aux applications de prendre le contrôle du volume d'un programme après des événements qui termineraient sinon son exécution. Bien que la GSE fonctionne avec des fichiers sources C++, il n'est pas spécifiquement conçus pour C++. Si vous utilisez la GSE dans un programme C++ que vous compilez en utilisant l'option /EH avec certains modificateurs - des destructeurs d'objets locaux sont appelés mais d'autres comportements d'exécution non prévus peuvent apparaitre. (Pour une illustration, consultez l' exemple présenté ultérieurement dans cet article.) Dans la plupart des cas, au lieu de la GSE nous recommandons d'utiliser l'ISO- standard Gestion des exceptions C++, que Visual C++ prend également en charge. En utilisant la gestion des exceptions C++, vous pouvez garantir que votre code sera plus portable, et vous pourrez gérer les exceptions de tout type.

Si vous avez des modules C qui utilisent le GSE, vous pouvez les mélanger avec les modules C++ qui utilisent la gestion des exceptions C++. Pour plus d'informations, consultez Différences de gestion des exceptions.

Il y a deux mécanismes de GSE:

Ces deux genres de gestionnaires sont distincts, mais sont étroitement liés via un processus appelé « déroulement de la pile ». Lorsqu'une exception se produit, Windows cherche le gestionnaire d'exceptions le plus récemment installé qui est actif. Le gestionnaire peut effectuer l'une des trois choses suivantes:

  • Échoue à reconnaître l'exception et donne le contrôle aux autres gestionnaires.

  • Identifie l'exception mais la rejette.

  • Identifie l'exception et la gère.

Le gestionnaire d'exceptions qui reconnait l'exception peut ne pas être dans la fonction qui s'exécutait lorsque l'exception s'est produite. Dans certains cas, il peut être dans une fonction beaucoup plus élevée dans la pile. La fonction en cours d'exécution et toutes les autres fonctions dans le frame de pile sont terminées. Pendant ce processus, la pile « est déroulée » ; autrement dit, les variables locales des fonctions terminées - à moins qu'elles soient static— sont désactivées de la pile.

Tout en déroulant la pile, le système d'exploitation appelle tous les gestionnaires de terminaison que vous avez entrés pour chaque fonction. En utilisant un gestionnaire de terminaisons, vous pouvez nettoyer les ressources qui autrement resteraient ouvertes en raison d'un achèvement anormal. Si vous entrez dans une section critique, vous pouvez quitter via le gestionnaire de terminaisons. Si le programme doit s'arrêter, vous pouvez effectuer d'autres tâches de gestion interne telles que fermer et supprimer des fichiers temporaires.

Pour plus d'informations, consultez :

Exemple

Comme indiqué précédemment, les destructeurs d'objets locaux sont appelés si vous utilisez le GSE dans un programme C++ et le compilez en utilisant l'option /EH avec certains modificateurs - par exemple, /EHsc et /EHa. Toutefois, le comportement pendant l'exécution peut ne pas être celui prévu si vous utilisez également des exceptions C++. L'exemple suivant illustre ces différences de comportement.

#include <stdio.h>
#include <Windows.h>
#include <exception>
 
class TestClass
{
public:
    ~TestClass()
    {
        printf("Destroying TestClass!\r\n");
    }
};
 
__declspec(noinline) void TestCPPEX()
{
#ifdef CPPEX
    printf("Throwing C++ exception\r\n");
    throw std::exception("");
#else
    printf("Triggering SEH exception\r\n");
    volatile int *pInt = 0x00000000;
    *pInt = 20;
#endif
}
 
__declspec(noinline) void TestExceptions()
{
    TestClass d;
    TestCPPEX();
}
 
int main()
{
    __try
    {
        TestExceptions();
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        printf("Executing SEH __except block\r\n");
    }
 
    return 0;
}

Si vous utilisez /EHsc pour compiler ce code mais que le contrôle CPPEX de test local n'est pas défini, il n'y aura pas d'exécution du destructeur de TestClass et la sortie ressemblera à ceci :

  

Si vous utilisez /EHsc pour compiler le code et que CPPEX est défini à l'aide de /DCPPEX (afin que l'exception C++ soit levée), le destructeur de TestClass s'exécute et la sortie ressemble à ceci :

  

Si vous utilisez /EHa pour compiler le code, le destructeur de TestClass s'exécute indépendamment du levage de l'exception en utilisant std::throw ou à l'aide du GSE pour déclencher l'exception (CPPEX défini ou non). Voici un exemple de la syntaxe utilisée :

  

Pour plus d'informations, consultez /EH (Modèle de gestion des exceptions).

Voir aussi

Référence

Gestion des exceptions en Visual C++

Mots clés C++

<exception>

Concepts

Gestion des erreurs et des exceptions (Modern C++)

Autres ressources

Gestion structurée des exceptions (Windows)