SEH et Exceptions en C++
Qui ne s'est pas déja retrouvé devant une horrible boite de dialogue de crash de programme ressemblant à celle ci :
"The memory cannot be 'written' at 0x404459FF".
Cette boite de dialogue, bien connue des utilisateurs et développeurs sur Windows, est le résultat d'une exception matérielle, souvent la conséquence d'un problème logiciel comme un deréférencement de pointeur nul. Il existe bien entendu beaucoup raisons pour lesquelles cette boite peut apparaitre comme des Access Violation, Divide By Zero, Invalid Operation, Float Overflow et Underflow, Privileged Instruction,... autant de problèmes potentiels qui peuvent arrêter l'exécution d'un programme.
Le C++ met à disposition un mécanisme de gestion des exceptions par l'intermédiaire des mots clés try et catch tout en utilisant le handler par défaut. Ce handler (catch(...)) du mécanisme standard n'est cependant pas le plus intéressant car il ne permet pas d'obtenir le contexte d'exécution du processeur lors de la génération de l'exception, ni d'en savoir la cause.
Windows met à disposition du développeur une API spécifique permettant d'utiliser le Structured Exception Handling. Cela permet, entre autres, d'afficher une boite de dialogue à l'utilisateur avec un minimum d'informations de debug. Il est également possible d'avoir un StackTrace pour un exécutable en utilisant les informations de debug séparées. Contrairement à une idée recue, il est possible de génerer avec visual studio des informations de debug pour un exécutable optimisé avec /Ox par exemple.
Le compilateur C++ met à disposition un certain nombre de nouveaux mots clés permettant de protéger une section de code ou bien un programme entier et d'en intercepter les exceptions d'une manière plus efficace. On peut notamment utiliser __try, __except et __finally qui spécifiques au compilateur Microsoft. Cette méthode n'est cependant pas la plus simple à utiliser.
L'utilisation du SEH ici est couplée à l'utilisation de la librairie imagehlp.dll. Cette librairie permet d'explorer les fichiers de symboles (pdb) pour déterminer par exemple les fonctions trouvées en parcourant la StackFrame.
Cet article du MSJournal de 1997 ainsi que celui ci, (Oui, oui, 1997...) décrit assez bien le fonctionnement et l'utilisation du SEH au travers de l'utilisation d'une instance de classe en singleton, mais ce mode ne permet pas de rendre simplement la main au dernier handler d'exception présent.
Dans ce code, un mélange de l'utilisation du SEH et des exceptions du C++ permet de protéger des morceaux de code dans des exceptions C++ standard. L'utilisation de la fonction _set_se_translator permet d'enregistrer une fonction appelée lorsqu'une exception survient et de transformer ces exception Win32 en exceptions C++. L'exemple ici n'est pas parfait puisque l'original est utilisé dans le cadre de la protection d'un programme complet (tout le main en quelque sorte). Il se pourrait qu'une exception SEH lancée en dehors d'un bloc protégé génère tout de même la boite de dialogue de plantage générique.
Quoi qu'il en soit, l'utilisation de ce code est assez simple, il faut juste noter qu'il est nécessaire d'activer la génération des StackFrames (désactiver Omit Stack Frames) ainsi que des informations de debug sous forme de fichier extérieur (Debug Information Format : Program Database). Il faut noter également qu'il est indispensable de distribuer le fichier imagehlp.dll et les fichiers pdb (Program Database) avec l'executable pour avoir l'affichage de la StackTrace lors d'une exception.
Bien entendu pour la distribution publique d'un programme, on ne publiera pas les fichiers pdb, qui contiennent énormément d'informations.
Il faut noter également qu'il est possible d'utiliser le SEH sous sa forme "native", en C. Vous aurez simplement à supprimer les fonctionnalités spécifique au C++ du code précédent. (Je donne l'info puisque certains utilisent encore ce langage... :p)
Bon debug !