Application Recovery and Restart

 
 
  • Gérald Barré

When an application crashes the user does not want to lose the data is was working on. Small bonus they would like the application to restart automatically and restore their context. This is exactly what allows the Application Recovery and Restart (ARR) API. ARR allows you to define a method to call when the application crashes, so you can save the current context, and also define whether to restart the application right away.

Let's start with some imports:

C#
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();

[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,
}

To indicate that the application must restart automatically, add the following lines:

C#
static void Main(string[] args)
{
    RecoveryAndRestart();

    if (args.Contains("/restart"))
    {
        // TODO restore the context
    }

    // TODO the code of your app
}

static void RecoveryAndRestart()
{
    if (Environment.OSVersion.Version.Major >= 6) // Windows Vista and above
    {
        RegisterApplicationRestart("/restart", RestartRestrictions.None);
    }
}

The feature is available only from Windows Vista, hence the verification of the version number of the OS. "/Restart" is the command line used to restart the application. It is thus possible to know if the application starts normally or after a crash. The second argument is used to specify when the application can restart automatically. RestartRestrictions.None indicates that the application must restart in all cases.

The save portion of the state is slightly more complicated (not too much anyway). The first step is to define the callback in case of error:

C#
static void Main(string[] args)
{
    RecoveryAndRestart();
}

private static void RecoveryAndRestart()
{
    if (Environment.OSVersion.Version.Major >= 6)
    {
        RegisterApplicationRecoveryCallback(ApplicationRecoveryCallback, IntPtr.Zero, pingInterval: 5000, flags: 0);
        RegisterApplicationRestart("/restart", RestartRestrictions.None);
    }
}

You can use the callback to save any user data that you can restore when restarting the application. You should also indicate regularly that the processing is in progress using ApplicationRecoveryInProgress, and also check if the user has requested the cancellation of the operation. Finally, you have to indicate you are ready to restart using ApplicationRecoveryFinished. The callback code looks like:

C#
private int ApplicationRecoveryCallback(IntPtr parameterData)
{
    try
    {
        // Notifies WER (Windows Error Reporting) regularly that the application is working.
        // The interval must be smaller than the value of "pingInterval" set during
        // the call to RegisterApplicationRecoveryCallback, otherwise WER will consider
        // the application as unresponsive and terminate it
        var pinger = new System.Timers.Timer(4000);
        pinger.Elapsed += (sender, args) =>
        {
            bool isCanceled;
            ApplicationRecoveryInProgress(out isCanceled);
            if (isCanceled)
            {
                Environment.Exit(2);
            }
        };

        pinger.Enabled = true;
        // TODO Save the state of the application
        File.WriteAllText("data.txt", "data");
    }
    finally
    {
        ApplicationRecoveryFinished(true);
    }

    return 0;
}

Now in case of a crash the following window is displayed:

Then the application restarts automatically:

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub