web-dev-qa-db-fra.com

C ++, __try et essayez / catch / enfin

Je m'interroge un peu sur les blocs C++ try/catch/finally. J'ai vu ces commandes avec deux traits de soulignement comme __try. Mais les projets MVSC 2010 fonctionnent également sans les soulignés. Alors, quand avez-vous besoin de ces soulignés?

42
martin

Sous Windows, les exceptions sont prises en charge au niveau du système d'exploitation. Appelés SEH (Structured Exception Handling), ils sont l'équivalent grossier des signaux Unix. Les compilateurs qui génèrent du code pour Windows en profitent généralement, ils utilisent l'infrastructure SEH pour implémenter les exceptions C++.

Conformément à la norme C++, les mots clés throw et catch ne lancent et n'attrapent que des exceptions C++. Le code d'exception SEH correspondant pour le compilateur MSVC est 0xe06d7343. Les 3 derniers octets sont le code ASCII pour "msc".

L'unification avec la prise en charge du système d'exploitation signifie également que les destructeurs C++ seront appelés lors du déroulement de la pile pour une exception SEH. Le code qui effectue le déroulement est à l'intérieur de Windows et traite le SEH déclenché par un throw exactement de la même manière que n'importe quel SEH. Cependant, le compilateur Microsoft dispose d'une optimisation qui essaie d'éviter de générer le code requis qui garantit que les destructeurs sont appelés dans tous les cas. S'il peut prouver qu'il n'y a aucune instruction throw à l'intérieur du bloc de portée qui contrôle la durée de vie de l'objet, il saute le code d'enregistrement. Ceci n'est pas compatible avec les exceptions SEH asynchrones, vous devez utiliser l'option de compilation/EHa pour supprimer cette optimisation si vous avez l'intention de capturer les exceptions SEH.

Il existe de nombreux types d'exceptions SEH. Celles qui peuvent être générées par le système d'exploitation sont répertoriées dans le fichier d'en-tête SDK ntstatus.h. De plus, vous pouvez interagir avec du code qui utilise SEH pour implémenter leur propre gestion des exceptions, ils utiliseront leur propre code d'exception. Comme .NET, les exceptions gérées utilisent le code d'exception 0xe0434f4d ("com").

Pour intercepter les exceptions SEH dans un programme C++, vous devez utiliser le mot clé __try non standard. Le mot clé __except est analogue au mot clé C++ catch. Il a plus de capacités, vous spécifiez une expression de filtre d'exception qui détermine si une exception active doit être interceptée ou non. Tout est possible, mais vous ne regardez généralement que les informations d'exception transmises pour voir si vous êtes intéressé à les gérer. Le mot clé __finally vous permet d'écrire du code qui s'exécute après le traitement de l'exception. Pas d'équivalent pour cela en C++ mais pas rare dans d'autres langages.

Tout cela est assez mal documenté comme le soulignent les commentaires. La preuve est dans le pudding. Voici un exemple de programme avec lequel vous pouvez jouer. Il montre comment les exceptions SEH permettent toujours d'appeler des destructeurs C++, à condition de compiler avec/EHa et comment les exceptions C++ sont implémentées au-dessus de SEH. Compilateur MSVC requis, exécutez avec Ctrl + F5 pour éviter que le débogueur ne soit utile:

#include "stdafx.h"
#include <windows.h>
#include <iostream>

// NOTE: the value of the C/C++, Code Generation, Enable C++ Exceptions setting in important
// Try it both with /EHsc (the default) and /EHa to see the difference

class Example {  
public:
    ~Example() { std::cout << "destructed" << std::endl; }
};

int filterException(int code, PEXCEPTION_POINTERS ex) {
    std::cout << "Filtering " << std::hex << code << std::endl;
    return EXCEPTION_EXECUTE_HANDLER;
}

void testProcessorFault() {
    Example e;
    int* p = 0;
    *p = 42;
}

void testCppException() {
    Example e;
    throw 42;
}

int main()
{
    __try {
        testProcessorFault();
    }
    __except(filterException(GetExceptionCode(), GetExceptionInformation())) {
        std::cout << "caught" << std::endl;
    }
    __try {
        testCppException();
    }
    __except(filterException(GetExceptionCode(), GetExceptionInformation())) {
        std::cout << "caught" << std::endl;
    }
    return 0;
}

Sortie:

Filtering c0000005
destructed
caught
Filtering e06d7363
destructed
caught
88
Hans Passant

__try/__except sert à intercepter SEH (erreurs générées par Windows) pas à intercepter les exceptions générales.

try/catch est ce que la norme C++ spécifie pour la gestion des exceptions C++ générales.

Pour le code C++ standard que vous écrivez, vous devez toujours utiliser try/catch et non __try/__except

De plus, finally n'est pas une construction spécifiée par C++ Standard, cela fonctionne pour vous car c'est un extension du compilateur Microsoft.

22
Alok Save

__try/__exceptest spécifique à Microsoft Si vous voulez que votre code soit compilable avec d'autres compilateurs (pour examplec g ++) (ou) dans un autre système d'exploitation, évitez de les utiliser et respectez le standard try/catch instructions

3
Ioan Paul Pirau