Apparu sous Windows Vista et Windows Server 2008, l’API Application Recovery & Restart (ARR) vous permet de « ressusciter » vos applications.
Vous pouvez utiliser cette API dans le cas où vous voulez sauvegarder les données et l’état de votre application avant que celle-ci ne plante complètement (exception, freeze de l’interface… etc).
Pour illustrer cette API, je vais utiliser un exemple basique d’un champ de texte multiligne où des données peuvent être saisies par l’utilisateur et un bouton permettant de simuler le crash de l’application.
En premier lieu nous allons faire un peu d’interop :
internal static class ApplicationRecoveryNativeMethods
{
internal delegate int ApplicationRecoveryCallback(IntPtr pvParameter);
[DllImport("kernel32.dll")]
internal static extern void ApplicationRecoveryFinished
([MarshalAs(UnmanagedType.Bool)] bool success);
[DllImport("kernel32.dll")]
[PreserveSig]
internal static extern uint ApplicationRecoveryInProgress
([Out, MarshalAs(UnmanagedType.Bool)] out bool canceled);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
[PreserveSig]
internal static extern uint RegisterApplicationRecoveryCallback
(IntPtr pRecoveryCallback, IntPtr param, uint pingInterval, uint flags);
[DllImport("kernel32.dll")]
[PreserveSig]
internal static extern uint RegisterApplicationRestart
([MarshalAs(UnmanagedType.BStr)] string commandLineArgs, RestartFlags flags);
}
Ensuite, j’ai déclaré une classe en static qui simplifie l’accès à ces méthodes :
public static class ApplicationRecovery
{
// Renvoie cette chaîne lors du redémarrage de l'application suite au plantage
public const string COMMAND_LINE = "{8B2A75FA-FFD6-487A-A27B-3C28A9243C46}";
public delegate void ApplicationCrashHandler();
public static event ApplicationCrashHandler OnApplicationCrash;
private static
ApplicationRecoveryNativeMethods.ApplicationRecoveryCallback RecoverApplication;
public static bool RegisterApplicationRestart()
{
uint val = ApplicationRecoveryNativeMethods
.RegisterApplicationRestart(COMMAND_LINE, RestartFlags.NONE);
if (val == 0)
{
// On indique le callback
RecoverApplication =
new ApplicationRecoveryNativeMethods.ApplicationRecoveryCallback(Recovery);
// On enregistre le callback et on indique au système le temps
// approximatif de recovery (ici 30sec)
val = ApplicationRecoveryNativeMethods.RegisterApplicationRecoveryCallback(
Marshal.GetFunctionPointerForDelegate(RecoverApplication),
IntPtr.Zero, 30000, 0);
}
return val == 0;
}
private static int Recovery(IntPtr ptr)
{
if (OnApplicationCrash != null)
{
OnApplicationCrash();
// Indique que tout est fini
ApplicationRecoveryNativeMethods.ApplicationRecoveryFinished(true);
}
return 0;
}
}
Et ensuite dans mon projet exemple, j’utilise la classe ci-dessus :
// On peut aussi tout simplement se baser sur le fichier de recovery
// s'il existe ou non
//bool isRecovery = false;
//if (File.Exists("recovery_xxxxxx.xxx"))
//{
// isRecovery = true;
//}
bool isRecovery = false;
if (args.Length > 0)
{
if (args[0] == Recovery.ApplicationRecovery.COMMAND_LINE)
{
isRecovery = true;
}
}
MainWindow win = new MainWindow(isRecovery);
Recovery.ApplicationRecovery.OnApplicationCrash +=
new Recovery.ApplicationRecovery.ApplicationCrashHandler
(win.ApplicationRecovery_OnApplicationCrash);
if (!Recovery.ApplicationRecovery.RegisterApplicationRestart())
{
MessageBox.Show("Error blabla");
}
SampleApplication.App app = new SampleApplication.App();
app.Run(win);
Note : pour des raisons « pratiques », la restauration et le redémarrage de l’application n’est possible qu’au bout de 60 sec (sinon, si dans le cas où ça plante direct au démarrage, on redémarre en boucle).
Télécharger mon projet exemple ici.
A bientôt,
Mathieu