Introducing the Updater Application Block
The Updater block lets you deploy updates to a central deployment server and have all of your .NET Windows Forms applications automatically check for and download updated files. For each update, you deploy all the new version's files to a folder on the deployment server, which contains a configuration file known as the server manifest. On the client, a small application, AppStart.exe, serves as a controller that launches an instance of your application, loads information about the installed version from a client-side manifest, and periodically polls the server for the availability of updates. This entire process is transparent to users, who don't have to do anything special to receive or install application updates. When AppStart.exe discovers an update, it downloads the updated files on a background thread, minimizing disruption to the user.
The Resources section provides a link to the Updater block's documentation on MSDN. The documentation provides a comprehensive overview of how the block works, and the basics of how to implement it. Although the documentation is good, there's a lot of it, and the block can be tricky to configure and get working. There are also one or two issues with the way the block has been designed which you'll need to resolve. Fortunately, the block comes complete with source code, so you'll see how to customize AppStart.exe to provide the features you need for your own applications.
Initial Setup
To begin, download the Updater block installer and run it to install the documentation and source code. Note that the installer installs both VB.NET source and C# source even though this article uses only C# for the examples. You will need to integrate the Updater Block source code with your own Windows Forms solution, so take a copy of the source from the following location :
<span class="pf">C:\Program Files\Microsoft Application Blocks for
.NET\Updater\Code\CS\
Microsoft.ApplicationBlocks.Updater</span>
This folder contains the source code directories listed in Table 1, which you should copy into your application's solution folder:| Folder | Notes |
| AppStart | Contains code for the AppStart assembly, which is the host process that will run your application and manage polling of the update server and download of files. References the ApplicationUpdater assembly. |
| Microsoft.ApplicationBlocks.ApplicationUpdater | The main Updater assembly. |
| Microsoft.ApplicationBlocks.ApplicationUpdater.Interfaces | Referenced by the Updater assembly, and used to provide the base interfaces for creating custom downloaders, post-processors, and validators to extend the application block. |
| Microsoft.ApplicationBlocks.ExceptionManagement & Microsoft.ApplicationBlocks.ExceptionManagement.Interfaces | The Exception Management application block assemblies, which are referenced by AppStart and the Updater assembly. |
It's All About AppStart.exe
As mentioned earlier, the Updater block comes with an application called AppStart.exe which serves as a host process that loads configuration data, launches your application, and invokes the Updater block code to do its work. This means that, instead of launching your application directly, you need the user to launch AppStart, which in turn will locate and load your application's .exe file using configuration data from the AppStart assembly's app.config file.
![]() | |
| Figure 1. Updater Directory Structure: You must install your application with a specific directory structure for the Application Update Block to work properly. |
![]() | |
| Figure 2. Application Directory Structure: During the installation, place your application's files in a directory named according to the current version of your application. |
| Figure 3. Setup Project: Add the Primary Output and Content Files from AppStart to the Application Folder for the target machine. |
| Figure 4. Installation Application Folder: In the install project, create a folder named the same as your application's version number, and add the Primary Output from your project to that folder. |
| Figure 5. Creating Shortcuts: Rename your shortcut appropriately and drag it into the User's Desktop and User's Programs Menu folders in the Setup project. |
Configuring AppStart
Because AppStart is responsible for launching the latest version of your application, and polling the deployment server for updates, it requires a hefty set of configuration data before it will work. This is possibly the trickiest part of the process, as there are numerous settings and each must be configured exactly right. On top of this, there are a few "gotchas" and problems you will need to work through.
The Updater block documentation lists several ways to configure AppStart, but by far the easiest and most flexible approach is to put the configuration for both AppStart and the Application Updater assembly into a single app.config file in the AppStart project. You need to add the app.config file yourself (Add | New Item | Application Configuration File).
The documentation for the block lists the required settings in some detail, so I won't reproduce them here, but you should note the following caveats:
The AppStart app.config holds the configuration settings for both the AppStart and AppUpdater assemblies, so create sections for both assemblies in the <configSections> tag, as follows:
<configSections>
<section name="appStart"
type="Microsoft.ApplicationBlocks.
ApplicationUpdater.AppStart.
ConfigSectionHandler ,AppStart" />
<section name="appUpdater"
type="Microsoft.ApplicationBlocks.
ApplicationUpdater.UpdaterSectionHandler,
Microsoft.ApplicationBlocks.
ApplicationUpdater" />
</configSections>
In the <appStart> section you set configuration data about your installed application, including the name of your application's .exe file, the initial version (which you should explicitly set in the AssemblyInfo.cs file), and the last updated timestamp (just use the default setting defined in the documentation). <appStart>
<ClientApplicationInfo>
<appFolderName>\1.0.0.0</appFolderName>
<appExeName>WinFormsApplication.exe</appExeName>
<installedVersion>1.0.0.0</installedVersion>
<lastUpdated>
2003-05-04T14:49:18.4483296-05:00
</lastUpdated>
</ClientApplicationInfo>
</appStart>
Configuring the Updater
In the <appUpdater> section you specify the config data for the Updater block itself. As with the <appStart> section, your first point of reference when setting up this section should be the Updater block documentation, but the following tips should save you some time. The easiest parts to configure are the <polling> and <downloader> sections, which are self-explanatory. You should copy the default settings given in the documentation.
You can use a custom validator to ensure that downloaded code hasn't been tampered with by third parties. Doing so is both optional (and configurable with the useValidation attribute of the <application> element); however, omitting any of the <validator> settings generates a configuration exception, so you must include valid configuration data for this section. This is another flaw in the Updater block's designafter all, if you don't want to use validation, why should you have go to the bother of having to generate keys and set up the configuration? That said, it's highly recommended that you use validation for security reasons.
The Updater block comes with a useful and tastefully coloured tool called the Manifest Utility. The tool has a dual function: You use it to create the server manifests for your application's updates, and you can also use it to generate the public and private keys to configure the Updater block's validation functionality. Locate the utility's source (which is in the same place as the Updater code), and add it to your solution. Set the utility as the startup project and run it. Select the File | Generate Keys menu item. Doing that creates two XML files in a location of your choosing.
<application name="YourAlicationName"
useValidation="false">
<client>
<baseDir></baseDir>
<xmlFile>\AppStart.exe.config</xmlFile>
<tempDir>\Application Download Cache</tempDir>
</client>
<server>
<xmlFile>
http://YourDeploymentServer/YourRootFolder
/YourApplicationUpdateFolder/Manifest.xml
</xmlFile>
<xmlFileDest>\AppManifest.xml</xmlFileDest>
<maxWaitXmlFile>60000</maxWaitXmlFile>
</server>
</application>
The <server> element contains configuration data for the server manifest file; this is the XML file used to describe the content of each of your application's updates. You'll see how to create the manifest using the Manifest Utility later.
Deploying an Application Update
To deploy an update, you must create a server manifest file that contains information about the new version and its files. Before you generate the manifest, you should set up your deployment server and create a folder on a network share to contain your application updates. You should adopt the following pattern so that you and your release team can build an extensible framework for making application updates available on your network:
|---Root
| |---Application Name
| | |---Manifest.xml
| | |---<version #, e.g. 2.0.0.0>
| | | |---<application files>
Using this structure lets you set up a deployment server for multiple versions of multiple applications. It's sensible to give the folder that will hold your application files the same name as the new version numberbecause the Updater will download the files into an identically named folder on the client, and will help keep the multiple versions of your app's files neatly organized.![]() | |
| Figure 6. The Manifest Utility: This utility lets you specify options for the location and version of files used to update your application, along with RSA security to validate that the downloads requested by the Updater application haven't been tampered with. |
AppStart Overview
If you've worked along with this article, by now you should have set up and configured the Updater for your application and created a network share or virtual directory that you can use to deploy updates. Your app is almost ready to be deployed, but you need to complete the AppStart component provided by Microsoft with the block code so that it will launch the Updater block and manage downloads.
The AppStart code, as provided with the Updater block, is incomplete; AppStart's Main() method checks to make sure your app isn't already running, and launches your app in a new process. It doesn't contain any code to manage the Updater and download updates. This is to ensure that AppStart remains generic so it can be used in a variety of different deployment scenarios. Microsoft has provided several QuickStart samples with the Updater Block installation to illustrate each usage scenario.
As you're building a self-updating Windows Forms app, the Self Updating QuickStart is the place to begin (you'll find the code in the SelfUpdating2.cs file in the QuickStart SelfUpdating folder with the rest of the Updater block source). Have a look through the code for this version of AppStart and familiarize yourself with it; you'll need to create your own version of AppStart or your application, using this as a template. I'm not going to reproduce the code in the QuickStart hereI'll leave you to create your own AppStart using the information in the next few sections.
The first thing to notice is that AppStart launches the Updater on a new thread; the code in AppStart's Initialize() method calls into ApplicationUpdateManager.StartUpdater(). After launching, all subsequent communication is triggered via events raised from the Updater. You may be aware that Windows Forms controls aren't thread safe, which means that you can only access Win Forms controls from the thread on which they were created. To ensure that this rule isn't broken (which can lead to random deadlocks, InvalidOperation exceptions and other strange and difficult to reproduce behavior), the Updater events are handled using the AppStart form's Invoke() method which uses a delegate to a method that is guaranteed to be invoked on the UI thread. There is an excellent discussion of the Win Forms thread safety issue, along with sample code at: http://www.devx.com/dotnet/article/11358/0/page/3.
To summarize then, AppStart launch your application in a new process, which ensures that the user can continue on with working in your application while AppStart drives the Updater. The MS-supplied code lacks a means to exit AppStart when the user terminates your app; without this, AppStart will get "stranded", and continue running. To prevent this, you need to change the StartApp_Process() method in AppStart, as follows (note that I take no responsibility for the coding standards in use by the authors of the application blockfew of the MS App Blocks conform to the .NET Design Guidelines, and I usually have to exclude them from my FxCop projects):
ProcessStartInfo p = new ProcessStartInfo (_exePath);
p.WorkingDirectory = Path.GetDirectoryName(_exePath);
_process = Process.Start(p);
// Add the following lines to ensure that AppStart
// terminates when the app process exits:
_process.EnableRaisingEvents = true;
_process.Exited += new EventHandler(process_Exited);
The process_Exited() method (helpfully created for you by Visual Studio) should stop the Application Updater if it is running, and also kill the AppStart process with the line: Environment.Exit(0);.
Now that you've ensured that both AppStart and the Updater will be cleaned up when a user closes your application, you can get on with wiring up the Updater's events. The Updater events all provide an ApplicationUpdaterEventArgs class argument, which supplies comprehensive information about the new version, derived from the server manifest file. The available information includes the application name, the new version number, the location of the new version on the network, and even information about the files included in the update.| Event | Description | AppStart Action |
| UpdateAvailable | Fires when the Updater reads the server manifest for your app and the version number of the update is greater than the version installed. | Use this event to notify users that an update is available; you might display a MessageBox giving them the option of downloading the update (you should never force an update on the user), and perhaps provide a link to information about the benefits of upgrading to the new version. Note that unless you prevent it, the Updater will continue to run, downloading the updated files after raising the UpdateAvailable event. To prevent this, you must block the Updater thread in your handler for the event (the best way is to use a modal dialog or MessageBox), and call the ApplicationUpdateManager.StopUpdater() method if the user decides to reject the update. |
| DownloadStarted | Fires when the Updater begins to download the update. | You might want to take advantage of this method to provide feedback to the user on the progress of the download. To provide this kind of feedback, you ideally need to know how many files are to be downloaded, and some way of calculating how long it takes each to download. The ApplicationUpdaterEventArgs class provides you with a Files array from which you can obtain the number of files to be downloaded. It's a bit trickier to work out when each has been successfully downloaded, but you can use a FileSystemWatcher on the download temp directory (you set this in the <tempDir> element of the Updater <application> config section). Note that the downloader will generate .tmp files as it streams down the bits, and you may find that the FileWatcher will notify you two or three times per file as the .tmp files are downloaded and transformed into the actual application files. I recommend you wire up both the Created and Changed events of the FileSystemWatcher to ensure you receive proper notifications during the download. Using this information, you could set the Maximum property of a Progress Bar control to the number of files, and call the Increment() method in the handler for the FileSystemWatcher events. |
| DownloadCompleted | Fires when the application files have been downloaded and moved from the temporary download folder into the new application folder. | You can use this event to close down any dialogs you displayed to indicate download progress, and ask users if they want to close the application and launch the new version (the QuickStart shows you how to do this). Likewise, if your update made changes that require a reboot, this is the time to ask users if they want to reboot right away or later. |
Updater Block Design FlawEliminating Absolute Links
As you've seen, the block relies on absolute paths links being provided in the configuration file. But this reliance causes big problems, because you can't always guarantee that users will install your application to a specific location. Fortunately, fixing the application block is relatively simple using some basic Reflection techniques to locate the directory in which the Updater components are executing, and appending the relative link to the application name that you defined in the config file. Unfortunately, there are quite a few places where changes need to be made. Remember that the changes described in this section are purely optional, and should be undertaken only if you're determined to use relative links in the config file. If you can ensure that all the file paths in the config file are absolute links, everything will work just fine without these modifications.
Start by changing the first line in the StartApp_Process() method, which combines the <appFolderName> (supposedly containing an absolute path to the folder your app is installed in) and the <appExeName> settings defined in the AppStart config section.
_exePath = Path.Combine(
_config.FolderName , _config.ExePath );
You want to change the preceding line to use Reflection to obtain the name of the folder in which AppStart is executing, and to append the (relative) folder name and your application's executable name to it, as follows (if you remember, your app folder is a subfolder of the folder containing AppStart): string appFolder =
Path.GetDirectoryName(
Assembly.GetExecutingAssembly().
GetModules()[0].FullyQualifiedName);
_exePath = Path.Combine(appFolder +
_config.FolderName, _config.ExePath);
You'll fix the Updater in a similar way. Table 3 lists the location of the changes you should make:| File | Class | Member | Notes |
| UpdaterConfiguration.cs | ClientConfiguration | XmlFile set |
|
| BaseDir set | Remove the !Path.IsPathRooted check—it will throw an exception if you don't use an absolute link! | ||
| TempDir set | Again, remove the Path.IsPathRooted check. | ||
| ServerConfiguration | ServerManifestFileDestination set |
|
string folder = Path.GetDirectoryName(
Assembly.GetExecutingAssembly().
GetModules()[0].FullyQualifiedName);
string relativeLocation = folder + value;
_xmlFile = relativeLocation;
As you can see, this uses code similar to that used in AppStart to remove the dependency on an absolute path.
| DevX is a division of Internet.com. © Copyright 2010 Internet.com. All Rights Reserved. Legal Notices |