Implementing a Silent Updating Process

For many applications, it is not desirable to leave the decision of whether or not to update an application to the end-user. In fact, it is sometimes desirable to craft an updating process that operates silently, with no user interaction at all. AppLife Update makes it very easy to implement such an update process. In this blog post, I’ll walk through implementing a silent updating process.

The Process

When our demo application starts up, we’ll check for updates asynchronously in the background. If an update is available, we’ll asynchronously download the update in the background. To make the process the least intrusive to the user, our process won’t take any action when the download completes. Since the downloaded update package is cached locally as it downloads, it will be available the next time the application is restarted. Upon application startup, we’ll check to see if an update is fully downloaded and if so, go ahead and apply the update. If the update is not yet fully downloaded, the download will pick up where it left off.

Handling Unexpected Errors

Many things can go wrong as we check for and download updates over a network, and since our process is being performed silently, we’ll need to handle any errors that occur and decide what to do about it.

We’ll also need to be able to identify errors during the silent update execution. When an update is applied, our application is shutdown so that the application assemblies can be replaced. When the update process finishes, the application is restarted automatically. During startup, we’ll look at the results of the last update to see if any errors occurred. If any errors occurred, we can inform the user or send an administrative notice to support.

Now that we have a plan, we can go forward with implementation.

Implementation

With the desired behavior identified, we’ll implement this updating process using AppLife Update. To start, we’ll create a new Windows Forms application and add an Update Controller to the main form from the AppLife Update toolbox palette.

The Update Controller application programming interface (API) will provide all of the functionality we need to accomplish our identified update process. Once the update controller is added to your form, go ahead and set up a new project using the control smart tag. This will create an AppLife Update project that you’ll use to create and publish updates for your application.

Note: The Update Controller does not need to reside on a form. You can create an Update Controller and configure it programmatically in any .Net application.

Now that we have an Update Controller in the application and a project setup, we’ll implement our desired update process.

Checking For Updates

To check for updates, we’ll use the CheckForUpdateAsync method on the Update Controller. This check is performed asynchronously, and when the check completes, the CheckForUpdateCompleted event is raised.

   1: using Kjs.AppLife.Update.Controller;  
   2:    
   3:    
   4: public Form1() {  
   5:   InitializeComponent();  
   6:    
   7:   //check for updates  
   8:   updateController1.CheckForUpdateCompleted +=   
   9:    new CheckForUpdateCompletedEventHandler(  
  10:      updateController1_CheckForUpdateCompleted);  
  11:   updateController1.CheckForUpdateAsync();  
  12: }  
  13:    
  14: void updateController1_CheckForUpdateCompleted(object sender,  
  15:   CheckForUpdateCompletedEventArgs e) {  
  16:   if(e.Result == true) {   
  17:     //An update is availabe  
  18:    
  19:   }  
  20: }  

After the check completes, we can check to see if the update is already downloaded by inspecting the IsDownloaded property of the update. If the update is downloaded, we can go ahead and apply the update. If it is not, we’ll initiate the download. The download will pick up from where any previous instance left off. When the download process completes, the DownloadUpdateCompleted event will be raised. We won’t take any action when the download completes, but we will use this event for error handling later. We are not showing download progress in this implementation, but there is also a DownloadUpdateProgressChanged event to monitor download progress.

 1: public Form1() {  
   2:   InitializeComponent();  
   3:    
   4:   //check for updates  
   5:   updateController1.CheckForUpdateCompleted +=   
   6:    new CheckForUpdateCompletedEventHandler(  
   7:     updateController1_CheckForUpdateCompleted);  
   8:   updateController1.DownloadUpdateCompleted +=  
   9:     new AsyncCompletedEventHandler(  
  10:     updateController1_DownloadUpdateCompleted);  
  11:   updateController1.CheckForUpdateAsync();  
  12: }  
  13:    
  14: void updateController1_DownloadUpdateCompleted(object sender,  
  15:    AsyncCompletedEventArgs e) {  
  16:   //Download completed. Ready to apply on next startup.  
  17: }  
  18:    
  19: void updateController1_CheckForUpdateCompleted(object sender,  
  20:    CheckForUpdateCompletedEventArgs e) {  
  21:   if(e.Result == true) {   
  22:     //An update is available  
  23:     if(updateController1.CurrentUpdate.IsDownloaded == true) {  
  24:       //the update is already downloaded, go ahead and apply it.  
  25:     } else {   
  26:       //start the download  
  27:       updateController1.DownloadUpdateAsync();  
  28:     }  
  29:   }  
  30: }  

Applying the Update

With the update downloaded, we can apply the update. We want to apply the update silently, so we’ll pass in a parameter that prevents the update window from being displayed. This method call will close the application, launch the update process, and restart after the update completes.

   1: void updateController1_CheckForUpdateCompleted(object sender,   
   2:    CheckForUpdateCompletedEventArgs e) {  
   3:   if(e.Result == true) {   
   4:     //An update is availabe  
   5:     if(updateController1.CurrentUpdate.IsDownloaded == true) {  
   6:       //the update is already downloaded, go ahead and apply it.  
   7:       updateController1.ApplyUpdate(ApplyUpdateOptions.NoUpdateWindow);  
   8:     } else {   
   9:       //start the download  
  10:       updateController1.DownloadUpdateAsync();  
  11:     }  
  12:   }  

That’s it! We have implemented a silent update process, and in a perfect world we’d be done. But we don’t live in a perfect world, and unexpected events occur. Since this process is completely silent to the user, we’ll need to prepare for error conditions programmatically and take appropriate actions.

Error Handling

Handling errors that occur during the update check and download process is straightforward. The asynchronous event handlers can check for errors that occurred and look at the specifics of the error to aid in determining how to handle the error. If you are using the http protocol for updating, the inner exception is probably a WebException class, which provides additional error information. For instance, this information could be used to ignore connectivity errors. For this demonstration, we’ll display a message box if any errors occur during the update check or download process.

   1: void updateController1_DownloadUpdateCompleted(object sender,  
   2:    AsyncCompletedEventArgs e) {  
   3:   //Download completed. Ready to apply on next startup.  
   4:   if(e.Error != null) {  
   5:     //An error occurred.  
   6:     if(e.Error is DownloadException) {  
   7:       //A download exception is thrown when something unexpected happens.  
   8:       //The InnerException property will hold the exception that occurred.  
   9:       string errorMsg = "";  
  10:       if(e.Error.InnerException != null) {  
  11:         errorMsg = e.Error.InnerException.Message;  
  12:       } else {  
  13:         errorMsg = e.Error.Message;  
  14:       }  
  15:       MessageBox.Show(  
  16:        string.Format("An error occurred downloading an update: {0}",  
  17:         errorMsg));  
  18:     }  
  19:   }  
  20: }  
  21:    
  22: void updateController1_CheckForUpdateCompleted(object sender,  
  23:    CheckForUpdateCompletedEventArgs e) {  
  24:   if(e.Error != null) {  
  25:     //An error occurred.  
  26:     if(e.Error is DownloadException) {   
  27:       //A download exception is thrown when something unexpected happens.  
  28:       //The InnerException property will hold the exception that occurred.  
  29:       string errorMsg = "";  
  30:       if(e.Error.InnerException != null){  
  31:         errorMsg = e.Error.InnerException.Message;  
  32:       }else{  
  33:         errorMsg = e.Error.Message;  
  34:       }  
  35:       MessageBox.Show(  
  36:      string.Format("An error occurred checking for updates: {0}",  
  37:        errorMsg));   
  38:     }  
  39:   }  
  40:        …  
  41:   

Detecting a Failed Update Execution

As an update is executed, the application being updated is shutdown. When an update is executed with a user interface, any errors that occur would be presented to the user through the visual updating interface. Since we are applying this update silently, we will need to inform the user of any errors that occur another way. After the update completes, the application is restarted. We can use methods of the Update Controller to easily investigate the results of the last update that was applied. We’ll use this method to look up what happened, and inform the user of any errors that occurred.

   1: InitializeComponent();  
   2:    
   3:   //check for updates  
   4:   updateController1.CheckForUpdateCompleted +=   
   5:    new CheckForUpdateCompletedEventHandler(  
   6:     updateController1_CheckForUpdateCompleted);  
   7:   updateController1.DownloadUpdateCompleted +=   
   8:    new AsyncCompletedEventHandler(  
   9:     updateController1_DownloadUpdateCompleted);  
  10:     
  11:   //Read last update results  
  12:   LastUpdate lastUpdate = updateController1.ReadLastUpdateInformation();  
  13:   if(lastUpdate != null &&  
  14:    lastUpdate.Result == UpdateResult.Failure &&   
  15:    lastUpdate.StartingVersion == updateController1.Version) {  
  16:     //An update failed. Prompt the user.  
  17:     if(MessageBox.Show(string.Format(  
  18:    "An unexpected error occurred while silently applying an update:  
  19:   {0}\n\nWould you like to retry the update?",  
  20:     lastUpdate.ErrorText), "Update Error", MessageBoxButtons.YesNo) ==   
  21:     DialogResult.Yes) {   
  22:       //launch an interactive update  
  23:       updateController1.UpdateInteractive(this);  
  24:     }  
  25:   } else {   
  26:     updateController1.CheckForUpdateAsync();  
  27:   }  

Conclusion

Using the AppLife Update API, you can quickly and easily implement a non-interactive silent updating process that can be used in any .Net application. The application checks for, and downloads updates asynchronously. When the application launches and an update has been fully downloaded in a previous session, the update is applied silently. If any errors occur, the user is notified. The example can be easily extended and improved as necessary to fit specific updating requirements.