Resuscitate Your .NET Programs with the Application Recovery and Restart API

icrosoft has gone to great lengths to help you make your software applications more appealing to users. You can create rich user experiences with Windows Presentation Foundation, smooth web applications with ASP.NET AJAX, data friendly applications with LINQ, and much more. But when it comes to appeal, nothing beats an application that is always available and doesn’t suffer from data loss or corruption. Microsoft’s Application Recovery and Restart (ARR) API makes it easier than ever to add this level of appeal to your application. ARR gives you an opportunity to recover data and return to a consistent state when your application terminates or hangs, and it will optionally restart your application.

Application recovery is the process of saving and recovering the state of your application when it terminates unexpectedly or hangs. Application restart is the process of launching an application after it has terminated unexpectedly. The restart functionality in Application Recovery and Restart is independent of the recovery functionality, but the two complement each other well. You may recover an application without automatically restarting it, and you may automatically restart an application without recovering its data.

Microsoft released ARR with Windows Vista, but it’s particularly relevant in Windows Server 2008, because server applications typically require higher reliability and stability, and servers rarely have a human present to address problems when they arise.

To explore ARR, it’s easiest to start with an application destined to fail. You’ll need Visual Studio 2005 or 2008 running on Windows Vista or Windows Server 2008 to build and run the sample “DevX.ServerApplication” application described in this article. If you haven’t yet upgraded to one of these operating systems, but you have an MSDN subscription, you can use Virtual PC 2007 to create a virtual machine test lab.

Listing 1 shows how DevX.ServerApplication exercises the Application Recovery and Restart API.

?
Figure 1. DevX.ServerApplication in Action: The sample application exercises the Application Recovery and Restart API.

DevX.ServerApplication is a .NET console application (see Figure 1). Microsoft didn’t expose Application Recovery and Restart in the .NET Framework, so the first step to using it with .NET is writing a class to expose the relevant kernel32.dll functions. It turns out that Application Recovery and Restart is one of the more difficult APIs to marshal properly, so you can save valuable time by adding the ApplicationRecoveryRestart class to your application:

   using System;   using System.Runtime.InteropServices;   using System.Text;      namespace DevX.ServerApplication   {       public static class ApplicationRecoveryRestart       {           public delegate Int32 ApplicationRecoveryCallbackDelegate(               RecoveryInformation parameter);              [DllImport("kernel32.dll")]           public static extern void ApplicationRecoveryFinished(               Boolean success);              [DllImport("kernel32.dll")]           public static extern Int32 ApplicationRecoveryInProgress(               out Boolean canceled);              [DllImport("kernel32.dll")]           public static extern Int32 GetApplicationRecoveryCallback(               IntPtr process,               out ApplicationRecoveryCallbackDelegate recoveryCallback,               out RecoveryInformation parameter,               out UInt32 pingInterval,               out UInt32 flags);              [DllImport("kernel32.dll")]           public static extern Int32 GetApplicationRestartSettings(               IntPtr process,               StringBuilder commandLine,               ref UInt32 size,               out UInt32 flags);              [DllImport("kernel32.dll")]           public static extern Int32 RegisterApplicationRecoveryCallback(               ApplicationRecoveryCallbackDelegate recoveryCallback,               RecoveryInformation parameter,               UInt32 pingInterval,               UInt32 flags);              [DllImport("kernel32.dll")]           public static extern Int32 RegisterApplicationRestart(               String commandLineArgs,               UInt32 flags);              [DllImport("kernel32.dll")]           public static extern Int32 UnregisterApplicationRecoveryCallback();              [DllImport("kernel32.dll")]           public static extern Int32 UnregisterApplicationRestart();       }   }   

With the Application Recovery and Restart API exposed to .NET, you’re ready to write server applications capable of recovering from failures and restarting.

Registering for Application Recovery
Developers have always taken steps to minimize terminations and hangs without using Application Recovery and Restart, such as handling exceptions properly and avoiding deadlock, but it’s difficult to anticipate every source of failure. When your application terminates or hangs, the host operating system takes over?enter the Windows Error Reporting (WER) service.

WER detects application failures and takes steps to make it easier for Microsoft and third parties to see and troubleshoot application failure trends. If your application fails, WER displays the dialog box in Figure 2 while it collects information about the failure.

?
Figure 2. Application Failure. When a registered application fails, Windows Error Reporting (WER) collects information.

Run the sample application and then type “a” to abort the main thread and invoke WER.

   Thread.CurrentThread.Abort();

Aborting the main thread isn’t the only way to invoke WER; after registering an application, you’ll also see this dialog box whenever the application fails to handle an exception, doesn’t respond appropriately to a Windows close or shutdown message, or stops responding to Windows messages. The sample DevX.ServerApplication aborts the main thread to invoke WER because it’s hosted in a Windows console application, so it will always respond to Windows messages and close properly.

When WER has finished collecting information about the failure, it will give your application an opportunity to recover data and state before unloading the process from memory. Listing 1 demonstrates how to use the ApplicationRecoveryRestart.RegisterApplicationRecoveryCallback method to instruct WER to call your ApplicationRecoveryRestart.ApplicationRecoveryCallbackDelegate during the recovery process.

There is a lot going on in Listing 1; here’s a more detailed explanation of the required steps.

The first step in registering for application recovery is to properly declare the ApplicationRecoveryCallbackDelegate in the ApplicationRecoveryRestart class. The Windows SDK C language declaration for the callback is in winbase.h:

   typedef DWORD (WINAPI *APPLICATION_RECOVERY_CALLBACK)      (PVOID pvParameter);   

When you expose this callback to your .NET application via a P/Invoke class it’s best practice to convert the PVOID parameter to a more specific type, because the conversion adds a layer of type safety to an otherwise ambiguous parameter that moves back and forth between your .NET application and kernel32.dll.

The following code defines a custom RecoveryInformation class that you can use for the parameter type in the application recovery callback delegate.

   // Custom recovery information class   public class RecoveryInformation   {       // Insert your custom recovery information here       public string Message;   }         // The recovery callback delegate as defined    // by Application Recovery and Restart   public static class ApplicationRecoveryRestart   {       public delegate Int32 ApplicationRecoveryCallbackDelegate(           RecoveryInformation parameter);       // ...   }   
Author’s Note: Plan to replace the RecoveryInformation class in the sample application with one that more closely satisfies your application’s recovery requirements.

The second step in registering for application recovery is to call the ApplicationRecoveryRestart.RegisterApplicationRecoveryCallback method, which accepts four parameters:

   [DllImport("kernel32.dll")]   public static extern Int32 RegisterApplicationRecoveryCallback(       ApplicationRecoveryCallbackDelegate recoveryCallback,       RecoveryInformation callbackParameter,       UInt32 pingInterval,       UInt32 flags);      

All this information gets sent to the callback when your application fails. The first two parameters are an instance of the callback delegate and an instance of the recovery information class. The pingInterval parameter determines how often (in milliseconds) WER sends a heartbeat to your application during recovery to make sure it’s responding. The recommended value is 5,000 milliseconds (5 seconds), while the maximum value is 5 x 60 x 1,000 milliseconds (5 minutes).

The fourth parameter is reserved for future use; pass in zero.

Author’s Note: As of this writing, the MSDN documentation for RegisterApplicationRecoveryCallback and GetApplicationRecoveryCallback incorrectly specifies the pingInterval parameter units as 100-nanosecond intervals.

The following methods demonstrate how to retrieve your application recovery settings with GetApplicationRecoveryCallback and reset your application recovery settings with UnregisterApplicationRecoveryCallback.

   // GetApplicationRecoveryCallback    // as defined by Application Recovery and Restart   public static extern Int32 GetApplicationRecoveryCallback(       IntPtr process,       out ApplicationRecoveryCallbackDelegate recoveryCallback,       out RecoveryInformation parameter,       out UInt32 pingInterval,       out UInt32 flags);      // Retrieving application recovery settings   ApplicationRecoveryRestart.ApplicationRecoveryCallbackDelegate        confirmRecoveryCallback = null;   RecoveryInformation confirmParameter;   UInt32 confirmPingInterval;   UInt32 confirmFlags;   ApplicationRecoveryRestart.GetApplicationRecoveryCallback(       Process.GetCurrentProcess().Handle,       out confirmRecoveryCallback,       out confirmParameter,       out confirmPingInterval,       out confirmFlags);      // Resetting application recovery settings   ApplicationRecoveryRestart.UnregisterApplicationRecoveryCallback();   

The next step is to implement your recovery callback.

Implementing Application Recovery
The most important task for your application recovery callback is to attempt to prevent data loss or corruption; only you know the exact mechanisms required to do so for your application. However, some of the tasks to consider implementing include:

  • Compare in-memory data to saved data to determine if there is a potential for loss or corruption. Save the recovered data if possible.
  • Log information about the failure and the application state.
  • Give the user an opportunity to take action or make any necessary decisions.

WER needs to know that you’re making progress while attempting to recover after an application failure. You must call ApplicationRecoveryInProgress within the ping interval you passed into RegisterApplicationRecoveryCallback. For example, if you passed in the recommended value of 5,000 for the ping interval, then your recovery callback must call ApplicationRecoveryInProgress every 5 seconds at a minimum. If you don’t, then WER will assume your application wasn’t able to recover and unload the process from memory.

   [DllImport("kernel32.dll")]   public static extern Int32 ApplicationRecoveryInProgress(       out Boolean canceled);
?
Figure 3. Recovery Dialog: Calling ApplicationRecoveryInProgress allows the user to see that recovery is under way and cancel the operation if necessary.

WER displays the dialog box in Figure 3 when you first call ApplicationRecoveryInProgress.

That dialog gives users the option of cancelling the recovery. If a user clicks Cancel while your application is trying to recover, the next call to ApplicationRecoveryInProgress will return true in the canceled parameter. You may choose to cancel recovery efforts if this is the case.

When you’re done recovering application data, call ApplicationRecoveryFinished to notify WER that your process is ready to be unloaded from memory.

   [DllImport("kernel32.dll")]   public static extern void ApplicationRecoveryFinished(       Boolean success);

Here’s how the DevX.ServerApplication sample program simulates recovery tasks:

   public static Int32 ApplicationRecoveryCallback(      RecoveryInformation parameter)   {       try       {           // Make sure the parameter arrived           Console.WriteLine("-- Invoking recovery callback --");           Console.WriteLine("The message passed in through " +               "the recovery information parameter is: {0}",                parameter.Message);              // Simulate the process of recovering data           bool canceled = false;           for (int i = 0; i < 5; i++)           {               ApplicationRecoveryRestart.                  ApplicationRecoveryInProgress(out canceled);               Console.WriteLine("Application is recovering...");                  if (canceled)               {                   Console.WriteLine(                       "User canceled application recovery.");                   break;               }               Thread.Sleep(1000);           }              // Notify Windows that recovery is complete           ApplicationRecoveryRestart.ApplicationRecoveryFinished(               !canceled);           Console.WriteLine("Application finished recovering.");           Console.WriteLine();       }       catch (Exception ex)       {           Console.WriteLine(ex);       }          return 0;   }

Figure 1 shows the output for the simulated application recovery.

Properly recovering your application data puts your application in a position to restart safely, and Windows can help.

Registering for Application Restart
Getting Windows to restart your application automatically after a failure follows a process similar to the recovery code you've already seen. First, you register the application for a restart in the event of an unexpected failure using the following code:

   // RegisterApplicationRestart as defined    // by Application Recovery and Restart   public static extern Int32 RegisterApplicationRestart(       String commandLineArgs,       UInt32 flags);      // Registering for restart in your application   string commandLineArgs = "/recovered";   Int32 result = ApplicationRecoveryRestart.      RegisterApplicationRestart(commandLineArgs, 0);   

The RegisterApplicationRestart method accepts two parameters. The first parameter is a string containing the command-line arguments Windows will pass to your application when it restarts. Do not include the executable in this parameter; Windows already knows what it is. You may pass in null if you don't need to provide command-line arguments. The second parameter is reserved for future use; pass in zero.

Take careful note of these two caveats before you move forward with testing and deploying an application registered for restart, because Windows will restart an application only:

  • After it has been running for 60 seconds or more. This is a safeguard against cyclical restarts?repeatedly restarting an application that fails on startup or shortly after.
  • When WER detects an unexpected termination or hang. See the Restart Manager API for information on how to restart applications during installation or update. Restarting an application after it has failed may warrant taking extra steps during startup and initialization. Use the command-line arguments parameter in RegisterApplicationRestart to signal this condition.
Author's Note: As of this writing, the MSDN documentation for the RegisterApplicationRestart method incorrectly states that Windows displays a confirmation dialog box before restarting an application. Happily, this isn't the case; your server applications will restart automatically when they're unattended.

?
Figure 4. Application Restart: After a failure, the sample application recovers and restarts in a new window.

The sample application notifies you when it has been running for 60 seconds. The background window in Figure 4 shows an application that ran for 60 seconds and then failed. The application in the foreground is the new instance that Windows launched automatically.

Also much like the recovery code, you can retrieve your application restart settings with a call to GetApplicationRestartSettings or reset them with UnregisterApplicationRestart.

   // GetApplicationRestartSettings as defined    // by Application Recovery and Restart   public static extern Int32       GetApplicationRestartSettings(      IntPtr process,      StringBuilder commandLine,      ref UInt32 size,      out UInt32 flags);      // Retrieving application restart settings in your application   UInt32 capacity = 256;   UInt32 flags;   StringBuilder commandLineArgs = new StringBuilder((int)capacity);   ApplicationRecoveryRestart.GetApplicationRestartSettings(       Process.GetCurrentProcess().Handle,       commandLineArgs,       ref capacity,       out flags);      // Resetting application restart settings      ApplicationRecoveryRestart.UnregisterApplicationRestart();   

Application Recovery and Restart provides time-saving functionality for developers looking to improve application reliability and stability. Use application recovery to make sure your application fails gracefully without losing or corrupting data. Use application restart to minimize downtime and increase your confidence in unattended server applications.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: