Depuis Windows Vista et Windows Server 2008, le système embarque un nouveau jeu d’API Windows Error Reporting (WER) qui permet la gestion d’un redémarrage et d’une sauvegarde en cas de plantage d’une application. Comment contrôle-t-on ce jeu d’API pour l’activer sur nos applications ?
Je vais vous le montrer en utilisant directement le jeu d’API se trouvant dans kernel.dll puis en .NET avec le Windows API Code Pack que vous trouvez à l’adresse suivante : http://code.msdn.microsoft.com/WindowsAPICodePack.
Ce jeu d’API met à notre disposition plusieurs API, les voici :
1 2 3 4 5 6 | HRESULT WINAPI RegisterApplicationRecoveryCallback( __in APPLICATION_RECOVERY_CALLBACK pRecoveryCallback, __in_opt PVOID pvParameter, __in DWORD dwPingInterval, __in DWORD dwFlags ); |
RegisterApplicationRecoveryCallback permet d’enregistrer un callback pour le WER qui serait appelé avant la fermeture de notre programme en cas de plantage.
1 2 3 4 | HRESULT WINAPI RegisterApplicationRestart( __in_opt PCWSTR pwzCommandline, __in DWORD dwFlags ); |
RegisterApplicationRestart permet de définir la ligne de commande pour redémarrer l’application et dans quel cas redémarrer.
Passons à un exemple minimaliste :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #include <Windows.h> #include <iostream> #include <fstream> #include <string> using namespace std; string s; DWORD WINAPI RecoveryHandler(PVOID o) { ofstream of("test.txt", ios::out); if(of) { of << s; of.close(); } ApplicationRecoveryFinished(TRUE); return 0; } DWORD main(int argc, char **argv) { if(argc != 1) { ifstream ifs("test.txt", ios::out); if(ifs) { ifs >> s; ifs.close(); cout << "Votre phrase a sauvegarder en cas de plantage etait : " << s << endl; } } RegisterApplicationRecoveryCallback(RecoveryHandler, NULL, RECOVERY_DEFAULT_PING_INTERVAL, 0); RegisterApplicationRestart(L"/restart", 0); cout << "Tapez une phrase a conserver en cas de plantage :" << endl; getline(cin, s); cout << "Attendez 60s pour que l'auto-restart s'active." << endl; Sleep(60000); cout << "Appuyez sur Entree pour provoquer le plantage." << endl; cin.peek(); throw new exception(); return 0; } |
Quelques explications s’imposent.
Le 3ème paramètre de l’API RegisterApplicationRecoveryCallback permet de spécifier l’intervalle de temps pour la sauvegarde des données, notre sauvegarde est très rapide donc nous n’avons rien de plus à gérer par contre en cas de gros traitements dans la fonction de sauvegarde, il faudrait notifier à Windows que notre fonction n’a pas figé en appelant régulièrement l’API ApplicationRecoveryInProgress.
Windows impose une vie de 60s du processus pour que l’auto-restart s’active, cette contrainte permet d’éviter des boucles en cas de plantage instantané du processus…
Quelques remarques avant de continuer :
- Il est possible d’appeler l’API RegisterApplicationRestart avec comme premier argument NULL puis de l’appeler avec une autre valeur, le timer de 60s restera à partir du premier appel.
- Il existe deux API pour récupérer les informations de Recovery ou de Restart, GetApplicationRecoveryCallback et GetApplicationRestartSettings.
- Il est possible d’annuler ces enregistrements au WER avec les API suivantes : UnregisterApplicationRecoveryCallback, UnregisterApplicationRestart.
Passons au .NET …
La librairie Windows API Code Pack nous fournit différentes méthodes statiques dans la classe ApplicationRestartRecoveryManager.
Voici, le même exemple qu’au-dessus en utilisant cette librairie :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #region Using(s) using System; using System.Diagnostics; using System.IO; using Microsoft.WindowsAPICodePack; using System.Linq; #endregion namespace RestartSample { internal class Program { private static string _saveThis = ""; private static void Main(string[] args) { if(args.Contains("/restart")) { _saveThis = new StreamReader("save.txt").ReadLine(); Console.WriteLine("Votre phrase à sauvegarder en cas de plantage était : " + _saveThis); } ApplicationRestartRecoveryManager.RegisterForApplicationRecovery(new RecoverySettings(new RecoveryData((RecoveryHandler), null), 1)); ApplicationRestartRecoveryManager.RegisterForApplicationRestart(new RestartSettings("/restart", RestartRestrictions.None)); Console.WriteLine("Tapez une phrase à conserver en cas de plantage :"); _saveThis = Console.ReadLine(); Console.WriteLine("Attendez 60s pour que l'auto-restart s'active."); System.Threading.Thread.Sleep(60000); Console.WriteLine("Appuyez sur Entrée pour provoquer le plantage."); Console.ReadLine(); Environment.FailFast(null); } public static int RecoveryHandler(object o) { StreamWriter streamWriter = new StreamWriter("save.txt"); streamWriter.WriteLine(_saveThis); streamWriter.Close(); ApplicationRestartRecoveryManager.ApplicationRecoveryFinished(true); return 0; } } } |
Voila, je pense que si vous avez suivi au dessus, ce code ne pose pas de soucis …
Pour conclure, je dirais que Microsoft nous simplifie extrêmement la gestion d’un processus autonome en nous proposant un jeu d’API complet et fonctionnel. Je vous invite à visiter cette page qui fournit plus d’explications sur ce jeu d’API.
N’hésitez pas me poser des questions par commentaires si vous rencontrez des soucis.
Vous pouvez trouver les codes sur le repository ici. Amusez-vous bien, mais faites pas tout planter !





0 réponses
Suivez la conversation, abonnez-vous au flux RSS des commentaires..