Comment puis-je garantir que le code de nettoyage s’exécute dans Windows C ++ (SIGINT, allocation incorrecte et fenêtre fermée)

J’ai un programme de console Windows C ++, et si je n’appelle pas ReleaseDriver() à la fin de mon programme, certains ReleaseDriver() entrent dans un mauvais état et ne peuvent plus être utilisés sans redémarrage. Je voudrais m’assurer que ReleaseDriver() est exécuté même si le programme se ferme anormalement, par exemple si j’appuie sur Ctrl+C ou ferme la fenêtre de la console.

Je peux utiliser signal() pour créer un gestionnaire de signal pour SIGINT . Cela fonctionne bien, même si à la fin du programme, une erreur agaçante apparaît: “Une exception Win32 non gérée s’est produite …”.

Je ne sais pas comment gérer le cas de la fermeture de la fenêtre de la console et, plus important encore, je ne sais pas comment gérer les exceptions provoquées par de mauvais access à la mémoire, etc.

Merci pour toute aide!

Sous Windows, vous pouvez créer un filtre d’exception non géré en appelant SetUnhandledExceptionFilter () . Une fois cela fait, chaque fois qu’une exception est générée qui n’est pas traitée quelque part dans votre application, votre gestionnaire sera appelé.

Votre gestionnaire peut être utilisé pour libérer des ressources, générer des fichiers de vidage (voir MiniDumpWriteDump ), ou tout ce dont vous avez besoin pour vous assurer que vous avez terminé.

Notez qu’il y a beaucoup de «pièges» dans la façon dont vous écrivez votre fonction de gestionnaire d’exceptions. En particulier:

  1. Vous ne pouvez appeler aucune fonction CRT, telle que new
  2. Vous ne pouvez pas effectuer d’allocation par stack
  3. Si vous faites quelque chose dans votre gestionnaire qui provoque une exception, Windows mettra immédiatement fin à votre application en extrayant les os de son dos. Vous n’avez plus aucune chance de vous arrêter avec grâce.

Vous pouvez appeler de nombreuses fonctions API Windows. Mais vous ne pouvez pas sprintf , new , delete … En bref, si ce n’est pas une fonction WINAPI, ce n’est probablement pas sûr.

En raison de tout ce qui précède, il est conseillé de faire toutes les variables de votre gestionnaire de fonction static variables static . Vous ne pourrez pas utiliser sprintf, vous devrez donc formater les chaînes à l’avance, lors de l’initialisation. Rappelez-vous simplement que la machine est dans un état très instable lorsque votre gestionnaire est appelé.

Si je ne me trompe pas, vous pouvez détecter si la console est fermée ou si le programme se termine par Ctrl + C avec SetConsoleCtrlHandler:

 #include  BOOL CtrlHandler(DWORD) { MessageBox(NULL, "Program closed", "Message", MB_ICONEXCLAMATION | MB_OK); exit(0); } int main() { SetConsoleCtrlHandler((PHANDLER_ROUTINE)&CtrlHandler, TRUE); while (true); } 

Si vous craignez des exceptions, comme bad_alloc, vous pouvez envelopper main dans un bloc try. Catch std::exception& qui devrait idéalement être la classe de base de toutes les exceptions levées, mais vous pouvez également intercepter toute exception C ++ avec catch (...) . Avec ces exceptions, cependant, tout n’est pas perdu et vous devez comprendre ce qui est lancé et pourquoi.

Éviter les comportements non définis aide également. 🙂

Vous ne pouvez pas (code de garantie exécuté). Vous pourriez perdre du pouvoir, alors rien ne fonctionnera. Le cache d’instruction L1 de votre CPU pourrait être bloqué, votre code échouera alors de manière aléatoire.

Le moyen le plus sûr d’exécuter du code de nettoyage est un processus distinct qui surveille la sortie du premier (juste WaitForSingleObject sur le handle du processus). Un processus de surveillance distinct est aussi proche que possible de la garantie (mais quelqu’un pourrait encore terminer TerminateProcess votre chien de garde).