Lorsqu’une application plante l’utilisateur ne souhaite pas perdre les informations sur lesquelles il travaillait. Petit bonus il souhaiterait que l’application redémarre automatiquement. C’est exactement ce que permet Application Recovery and Restart (ARR) et un peu de code :)
Dans les grandes lignes ARR permet de définir une méthode à appeler lorsque l’application plante et aussi de définir s’il faut redémarrer l’application tout de suite.
Commençons par quelques imports :
// Recovery
public delegate int RecoveryDelegate(IntPtr parameterData);
[DllImport("kernel32.dll")]
public static extern int RegisterApplicationRecoveryCallback(RecoveryDelegate recoveryCallback, IntPtr parameter, uint pingInterval, uint flags);
[DllImport("kernel32.dll")]
public static extern int ApplicationRecoveryInProgress(out bool canceled);
[DllImport("kernel32.dll")]
public static extern void ApplicationRecoveryFinished(bool success);
[DllImport("kernel32.dll")]
public static extern int UnregisterApplicationRecoveryCallback();
// Restart
[DllImport("kernel32.dll")]
public static extern int RegisterApplicationRestart([MarshalAs(UnmanagedType.LPWStr)] string commandLineArgs, RestartRestrictions flags);
[DllImport("kernel32.dll")]
public static extern int UnregisterApplicationRestart();
[Flags]
public enum RestartRestrictions
{
None = 0,
NotOnCrash = 1,
NotOnHang = 2,
NotOnPatch = 4,
NotOnReboot = 8
}
Pour indiquer que l’application doit redémarrer automatiquement il suffit d’ajouter les quelques lignes suivantes :
private void RecoveryAndRestart()
{
if (Environment.OSVersion.Version.Major >= 6) // Windows Vista et suivants
{
RegisterApplicationRestart("/restart", RestartRestrictions.None);
}
}
La fonctionnalité n’est disponible qu’à partir de Windows Vista d’où la vérification du numéro de version de l’OS. “/restart” correspond à la ligne de commande utilisée pour redémarrer l’application. Il est ainsi possible de savoir si l’application démarre normalement ou suite à un plantage. Le deuxième argument permet de spécifier dans quels cas l’application peut redémarrer automatiquement.
static void Main()
{
if (CommandLineUtilities.HasArgument("restart")) // Install-Package CodeFluentRuntimeClient
{
// TODO
}
}
RestartRestrictions. None indique que l’application doit redémarrer dans tous les cas. On peut choisir de ne pas redémarrer suite à un plantage, suite à un bloquage de l’application ou un redémarrage de l’OS.
Note : l’application ne redémarre que si elle a planté après plus de 60 secondes d’exécution. Cela permet d’éviter un redémarrage en boucle en cas d’erreur répétée.
La partie sauvegarde de l’état est légèrement plus compliqué (pas trop tout de même). La première étape est de définir le callback en cas d’erreur :
private void RecoveryAndRestart()
{
if (Environment.OSVersion.Version.Major >= 6) // Windows Vista et suivants
{
RegisterApplicationRecoveryCallback(ApplicationRecoveryCallback, IntPtr.Zero, pingInterval: 0, flags: 0);
RegisterApplicationRestart("/restart", RestartRestrictions.None);
}
}
Dans le callback il faut faire un traitement utile de sauvegarde de l’état de l’application. Il faut également indiquer régulièrement que le traitement est en cours (ApplicationRecoveryInProgress) et également vérifier si l’utilisateur a demandé l’annulation du traitement. Finalement il faut signaler la fin du traitement (ApplicationRecoveryFinished). Le code du callback ressemble à :
private int ApplicationRecoveryCallback(IntPtr parameterData)
{
try
{
// Notifie WER (Windows Error Reporting) régulièrement que l'application est en train de travailler
// L’interval doit être plus petit que la valeur de "pingInterval" défini lors
// de l’appel à RegisterApplicationRecoveryCallback sinon WER ne recevant
// pas de notification considèrera l’application comme morte et la terminera
var pinger = new System.Timers.Timer(4000);
pinger.Elapsed += (sender, args) =>
{
bool isCanceled;
ApplicationRecoveryInProgress(out isCanceled);
if (isCanceled)
{
Environment.Exit(2);
}
};
pinger.Enabled = true;
// Sauvegarde l'état de l’application
// Simule un traitement long...
Thread.Sleep(10000);
File.WriteAllText("data.txt", richTextBox1.Text);
}
finally
{
ApplicationRecoveryFinished(true);
}
return 0;
}
Maintenant en cas de plantage la fenêtre suivante s’affiche :
Puis l’application redémarre automatiquement :
Il ne vous reste plus qu’à traiter le redémarrage de l’application pour restaurer l’état de l’application et ainsi permettre à l’utilisateur de retrouver le même environnement qu’au moment du plantage.