Download
the .NET Application Updater Component
.NET Client Applications: .NET Application Updater Component
I recently received
a mail from the Microsoft IT team notifying me that they had detected several
applications on my desktop computer that did not have the latest patches
installed and instructed me to install the latest updates. Ill be the first to admit that I dont
update the applications I run as much as I should; either on my home machine or
on my work machines. It usually takes a
problem like a broken feature in an application or an email (or sometimes
several) from the IT department, to get me to install updates. Unfortunately Im more of the rule than the
exception when it comes to users updating their applications.
This
requirement of needing a user or admin to manually install an update is why
rolling out client updates has traditionally been such a huge problem and
expense. One solution is to move the
responsibility of updating the application from the user to the application
itself. Instead of the user obtaining
and installing a software update, the client application itself is responsible
for downloading and installing updates from a well known server. The only user interaction necessary is
whether or not they want to install the new updates now or later. You can see this type of approach to updating
applications in action today with products like Windows XP and Microsoft Money.
In this
article we will talk about an approach to building .NET client applications
that are able to automatically update themselves.
The .NET Application
Updater Component
Included
with this whitepaper is a component for enabling .NET client applications to
automatically update themselves. The
component was written using the .NET Framework and enables you to make your
application auto-updatable simply by dropping the component into your existing
.NET applications and setting a few properties (ex. where to get updates
from). Download the .NET
Application Updater Component.
This
component is not a Microsoft product. It
is intended as a sample to get you started and as such the source is also
included with this whitepaper. However,
it is worth mentioning that this component has gotten a fair amount of real
world use already. It has been used
internally in Microsoft to enable auto-updatability in the .NET Terrarium game. Terrarium has been installed and used by over
10,000 individuals since it was first unveiled as a beta product in October,
2001.
This
component will be the basis for the discussion of what it takes to make an
application auto-updatable. This paper
will focus on how the .NET Application Updater component works and how you can
use it in your own application.
Checking for Updates
The first
thing an application needs to be able to do in order to update itself is figure
out when a new update is available. In
order to do this an application needs to know three things 1) where to check
for updates, 2) when to check for updates and 3) how to check for updates. The application update component uses HTTP
for all network communication. This
allows it to update applications over the intranet or extranet. Thus the where to check for updates is
simply a URL to a well known Web server.
To address
the when to check for updates, the .NET Application Updater component creates
a thread on component creation which is responsible for checking for
updates. This thread will sleep for most
of the time, but will wake up at the configured interval to perform an update
check. How often the application checks
for new updates is dependent on the individual application, but common values
range from one hour to one day between update checks. This polling based approach is not
appropriate for all applications, for example Microsoft Money only checks for
updates when the user tells them to. In
this case, the update poller thread can be disabled and update checks performed
on-demand by calling the CheckForUpdate() method on the updater component.
There are
several ways to go about the how to check for updates:
Method #1: Direct File Check The simplest way to check for
updates is to use HTTP to compare the last modified date/time stamp of the application
files on the server with that on the client.
If the server has newer files, the client knows its time to update
itself. This is the same way a Web
browser knows if it needs to re-download an html page or image or whether it
can just re-use the one previously downloaded.
This is certainly the simplest to administrate. When a new version of the app is available,
the administrator simply copies the newer version over the older version on the
Web server. The problem with this is
that the update is not atomic and thus there are potential windows of
failure. For example, if an
administrator updates the version of the app on the Web server while a client
was in the middle of downloading the previous update, that client may be left
with some files from the previous update and some files from new update. For this reason, using a direct file check is
not recommended for any non-trivial applications.
Method #2: Manifest Check To solve the atomicity problem
with direct file checks a level of indirection is needed. To create a level of indirection a manifest file
on the server is used. A valid server
manifest file for use with the .NET Application Updater component looks like
this:
<VersionConfig>
<AvailableVersion>1.0.0.0</AvailableVersion>
<ApplicationUrl>http://localhost/demos/selfupdate/V1/</ApplicationUrl>
</VersionConfig>
AvailableVersion
specifies the assembly version number of the latest available version. The ApplicationURL property specifies the URL
where that version of the application resides.
When the administrator wants to update the client applications, they
would copy the new version of the app up to the Web server and modify the
server manifest file appropriately. The
client itself will then detect that the server manifest file has changed and
download the manifest file. The client then
compares the assembly version number specified in the manifest with the
assembly version number of the applications .exe file. If the server manifest file AvailableVersion
is newer, the application knows its time to perform an update. This approach has none of the atomicity
problems of the previous approach and is the recommended way to check for
updates for most applications.
Method #3: XML Web Service Check XML Web services provide a way to
perform more advanced update checks. For
example, suppose you wanted to roll out an update to a set of early adopters
before you rolled it out to the rest of your users. If the client application calls an XML Web
service to check if an update is available, that XML Web service could then
look up that user in a database and determine if the user is an early adopter
or not. If they are, the XML Web service
could return a value indicating that an update is available. If not, the Web service could return a value
indicating that a new update is not available.
Because the Web service used to check for updates could take many forms
depending on what custom functionality is desired, the .NET Application Updater
component does not provide direct XML Web service support. To use an XML Web service to check for
updates, first build the XML Web service and then hook the OnCheckForUpdate
event. This allows the poller threads
update check to be replaced with your own custom check. The OnCheckForUpdate event has a return value
to indicate whether an update was detected or not.
Downloading Updates
Once the
application determines a new update is available, the update needs to be
downloaded. By default, when the .NET Application
Updater component detects that a new update is available it will automatically
kick off another thread and start an asynchronous background download of the
update. The download is done using
HTTP-DAV. DAV is an extension to HTTP
that provides functionality such as directory and file enumeration. A complete and deep download is done starting
with the specified URL. The URL used to
download depends on the type of update check being done. For example, if a server manifest is used,
the URL to download the update from is specified by the ApplicationURL property
in the server manifest.
The
download of the update obviously needs to be robust. Leaving the client application in any kind of
bad state after the download and update is unacceptable. Any number of problems could occur during the
download: The Web server where the
update resides could go down, the client machine could crash, or for that
matter the user could simply shut down the application itself. Since the application is what is doing the
download, if its shut down, the download will stop. An alternative design would have been to use
a separate system service to do the download and update of the
application. With a system service, the
download of the update could continue even when the application itself is not
running. In fact, Windows XP has a
built-in download service called BITS for just this purpose. BITS is what Windows XP uses to download
updates to Windows itself. For more
information on BITS, see http://msdn.microsoft.com/library/en-us/dnwxp/html/WinXP_BITS.asp. A service was not used in the .NET Application
Updater component so that it would work on the Windows 9x operating systems
which do not support system services.
The .NET Application
Updater component achieves robustness by breaking the download and update
process into separate stages. As each
stage is complete, it is recorded in an updater manifest file which resides in
the application directory on the client.
If the download or update process is interrupted at any stage, it will
simply resume from the point of the last completed phase the next time the app
is started. Each stage is re-run able
so that if a failure occurs in the middle of a stage, re-running the stage from
start will succeed. If an error occurs,
such as the loss of connectivity with the Web server during a download, the .NET
Application Updater component will simply try again later. If enough failures are reported (ex. The Web
server never came back online), the download and update will be aborted and an
error reported.
Performing updates
Once the
new update has been downloaded, the last step is to apply the update. This last step is by far the most
challenging. The fundamental problem is
that the application is trying to update its own files while running. Thus the files are locked. The only way to unlock the files is to stop
the application, but if you stop the application, how can it update its files? Its a catch 22. We went through several designs before we
came up with our final approach. Before
I describe the final design, lets look at our first approach and describe its
shortcomings.
Our first
approach was to simply kick off a separate process to perform the update. This separate process would first shut down
the application process, perform the update (since the files were now unlocked),
restart the application process and then shut itself down when complete. However, there were three fundamental
problems with this design:
- It simply did not work in some
scenarios. For example, Windows
runs screen savers in what is called a Job object. The defining characteristic of a Job is
that when the root process of a Job is shut down, all processes it created
are also shut down. This makes
sense when you think about it. When
a screen saver goes away, you want to make sure it all goes away. However, in the application update case,
when the updater process shut down the original application process, the
updater process itself would also be shut down and thus no update performed.
- We wanted to be able to
automatically update all of the code that performed the update. In the course of use, we would find bugs
in the .NET Application Updater component, (ex. A timing window that would
effect 1% of clients), and we wanted the ability to automatically roll out
fixes of not just the application, but the .NET Application Updater
component as well. With this model,
we could not update the process that performed the update.
- Forcing the user to shut down
the application and wait in the middle of use was undesirable.
The final
approach used to perform the application update was inspired by the
side-by-side assembly model of the .NET Framework. Instead of trying to update the application
itself, create a new upto-date version of the application next to the existing
version. The new version can be created
by merging the existing application directory with the downloaded update. When the new version is complete, the user will
automatically use that version the next time they launch the application. The original copy of the application can then
be removed. This tricky part is figuring out which version to launch at any
given time. To do that, a program named
Appstart is introduced. Appstart is the entry point into your
application. With this model, your
application directory will look something like this:
Program Files
MyApp
Appstart.exe
Appstart.config
V1
Folder
MyApp.exe
V1.1 Folder
MyApp.exe
To run your
application, you always run Appstart.exe.
If you want a shortcut on the desktop, the shortcut should point at
Appstart, not at the application directly (Note that you can rename
AppStart.exe to be whatever you want, ex. YourApp.exe). Appstart.exe is a very simple program that
reads the Appstart.config file and launches the specified application. A valid Appstart.config file looks like the
following:
<Config>
<AppFolderName>V1 Folder</AppFolderName>
<AppExeName>MyApp.exe</AppExeName>
<AppLaunchMode>appdomain</AppLaunchMode>
</Config>
The
AppFolderName specifies the subfolder that contains the current version of the
app to run. The AppExeName contains the
name of the exe within that folder to launch.
When an application update is complete, the final step is to change the
AppFolderName value to point at the new version of the application. Thus the next time the user runs the
application, they will run the new updated version of the application. The AppLaunchMode specifies how to launch the
application. There are two ways to
launch the application. This first
approach is with AppDomains. AppDomains
are a feature of the .NET Framework common language runtime and the logical
unit of isolation and administration of objects. The common language runtime allows multiple
application domains per process. Thus
Appstart.exe can launch your application in a separate AppDomain but within the
same AppStart.exe process. Thus, despite
the fact that two different exes are running (in this case Appstart.exe and
MyApp.exe), only one process is used.
For most applications AppDomains will work well, however there are a few
minor differences with running in a separate AppDomain vs. a separate
process. For that case, the
AppLaunchMode can be set to process which will cause the application to
launch in a separate process.
Once
Appstart has started the application, it will go to sleep and wait for the
application to terminate. Once the
application terminates, Appstart will also shut down. However, if the application shuts down with a
well known return code, the Appstart process will re-read the Appstart.config
file and re-launch the application. This
is useful if the user wants to begin using the new version of the application
immediately after it is available.
Using the .NET
Application Updater Component
Now that
weve discussed how the .NET Application Updater works, lets put it in
action. First we will build a very
simple Windows Forms application. Then
we will enable it to auto-update itself using the .NET Application Updater component. Finally well deploy the application and see
the auto-update feature in action.
Included
with this paper is a zip file named DotNetUpdater.zip that contains the .NET
Application Updater component. The zip
file also contains a Sample directory which contains a number of files and
folders we will be using in this walkthrough.
Step 1: Build the application to update
In this
step we will build the application to auto-update. If you want, you can substitute in your own
application here. You can also use the
pre-built sample application included in the Samples\SampleApp\SampleApp
directory of the zip file. However for
the purpose of proving that there isnt anything special about SampleApp, well
walk through its creation.
1. Use Visual Studio .NET to create a
new Windows Application project, name it SampleApp.
2. Give the form an interesting
background color of your choice. We will
be using background color to differentiate between versions later.
3. Now lets add a tiny bit of
functionality to this application in the form of a button that opens a form residing
in a separate assembly. First add a
button to your form. The zip file
contains an assembly with a simple Windows Form in it. Add a reference to the Samples\SampleApp\SimpleForm
assembly in the zip. Then add two lines
of code to your button event handler:
SimpleForm.Form1 F = new SimpleForm.Form1();
F.Show();
4.
Switch
your build flag to build RELEASE instead of debug. This will allow us to avoid pdb file locking
problems later when we build a new version of the application while the
original copy is still running.
5.
Build
and test your application. It should
look similar to the Samples\SampleApp\SampleApp in the zip file.
Step 2: Add the .NET Application Updater Component
In this step
we will add the .NET Application Updater component to SampleApp.
1. In the components tab of the Visual
Studio .NET toolbox, right click and select Customize Toolbox. Select the
.NET Framework Components tab. Browse
and select the AppUpdater.dll in the AppUpdater project included in the
zip. Click OK.
2. An AppUpdater icon should now show
up at the bottom of the list of components in the toolbox. Drag and drop the AppUpdater component onto
the SampleApp Form. An appUpdater1 instance
of the .NET Application Updater component should be instantiated and appear
below the form.
Step 3: Configure the .NET Application Updater
Component
In this
step we will configure the .NET Application Updater component. To do this, select the appUpdater1 component
and open its properties. The following
section contains a description of each property and what value to set it
to. Note that you only need to change
the first four properties for this example, for the rest, the defaults are
adequate.
AppUpdater Properties These are the core properties of
the .NET Application Updater and will need to be set for this application as
follows:
|
Property Name
|
Description
|
|
AutoFileLoad
|
This
controls the on-demand download feature described later, for now set this to
true.
|
|
ChangeDetectionMode
|
This enum
determines how to check for updates.
In this example, we will use a server manifest check, so set this
value to ServerManifestCheck.
|
|
ShowDefaultUI
|
The .NET Application
Updater component has a set of simple UI for notifying the user of events
such as a new update becoming available or errors during updates. This UI can be replaced with custom
application specific UI by disabling the default UI, hooking the appropriate
events (ex. OnUpdateComplete) and popping up the custom UI. For this example we will use the default
UI, so set this value to true.
|
|
UpdateUrl
|
The
UpdateUrl is what determines where the updater looks for updates. In this case we are using a server manifest
to check for updates, so this property should be set to the URL of the server
manifest. For this example, set it to http://yourWebserver/SampleApp_ServerSetup/UpdateVersion.xml. Replace yourWebserver with the name of
your Web server.
|
Downloader Properties The
AppUpdater component has two sub-components.
The first is called the Downloader and controls the download and
installation of the component. Below is
a description of the properties, but all defaults will work fine for our
SampleApp.
|
Property Name
|
Description
|
|
DownloadRetryAttempts
|
If a failure
occurs during download (ex the Web server goes down) the downloader will try
again a little later. This property
controls the number of times the downloader will retry the network request
before treating it as a complete application update failure.
|
|
SecondsBeteweenDownloadRety
|
The
number of seconds before retrying the network request.
|
|
UpdateRetryAttempts
|
If a
serious error occurs during the update process, (ex. The downloader has
exceeded the DownloadRetryAttempts), an application update error is
generated. By default, the update
attempt will stop, but will attempt to resume the next time the application
is started (ex maybe the update Web server was just down for day). This property controls how many times an
update will be attempted. If this
value is exceeded, the updater aborts the update, resets its state and goes
back to checking for updates.
|
|
ValidateAssemblies
|
This
controls the level of validation done on downloaded assemblies. See the security section of this paper for
more info.
|
Poller Properties The second sub-component of the
AppUpdater is the Poller. The Poller
controls the update checks. Below is a
description of the properties, but all defaults will work fine for our
SampleApp.
|
Property Name
|
Description
|
|
AutoStart
|
A Boolean
that controls whether or not the Poller should begin polling for updates on
application startup or whether it should wait until it is explicitly started
programmatically.
|
|
DownloadOnDetection
|
A Boolean
that controls whether or not the Poller starts a download of an update immediately after a
new update is found, or whether the download must be started explicitly by a
call to the DownloadUdpate() method.
|
|
InitialPollInterval
|
The
number of seconds after application startup before the first update check is
performed.
|
|
PollInterval
|
After the
first update check, the PollInterval controls the number of seconds between
each subsequent update check.
Note: By default this checks
every 30 seconds; clearly you will want to reduce the frequency for your
application.
|
When all is
said and done, your property grid should look the following:

The
Samples\SampleApp\SampleApp_Complete directory contains a version of the
application correctly setup.
Step 4: Build & Deploy V1 of the application to
the client
In this
step we will build the V1 version of the application and deploy it to the
client. The deployment is essentially
simulating what the install program for your application will do.
1. In the SampleApp project, open the
AssemblyInfo.cs file. Change the
AssemblyVersion value from "1.0.*" to 1.0.0.0. This will cause the built assembly to get
marked with a value of 1.0.0.0 instead of the ever increasing value Visual
Studio normally assigns.
2. Build the application.
3. Copy the Samples\SampleApp\SampleApp_ClientSetup
directory from the zip onto your local machine.
It doesnt matter where you copy it, however the program files directory
is the most realistic place to put it since that is where most applications get
installed. Youll notice that
SampleApp_ClientSetup directory already has AppStart.exe included. AppStart.config is already set to point into
the 1.0.0.0 directory and run SampleApp.exe.
4. Copy the complete SampleApp (Appupdater.dll,
SimpleForm.dll & SampleApp.exe) from the release build directory of
SampleApp to the SampleApp_ClientSetup\1.0.0.0 directory on your client.
At this
point a fully functional version of the application should be installed on
the client and executable by running AppStart.exe
Step 5: Setup the Web server
In this
step we will setup the Web server for use in rolling out application
updates. The .NET Application Updater
component uses HTTP-DAV to download the application update and thus requires a Web
server that supports HTTP-DAV. IIS 5.0
that comes with Windows 2000 and newer operating systems support HTTP-DAV.
1. Copy the
Samples/SampleApp_ServerSetup directory from the zip into the wwwroot directory
of your Web server. Note: Any location on your Web server can be used,
but be sure to update the UpdateUrl property accordingly. Notice that UpdateVersion.xml is already
setup for use as the server manifest file.
2. For completeness, copy the V1
version of SampleApp into the 1.0.0.0 folder of the Web server.
3. Enable IIS Directory Browsing for
the SampleApp_ServerSetup directory on your Web server. Since the .NET Application Updater component
enumerates the contents of directories during download, Directory Browsing
must be enabled. To do this, open the
Internet Information Services manager in the control panel. Select the SampleApp_ServerSetup folder and
open its properties. Select the
Directory Tab. Check Directory Browsing,
and click OK.
Step 6: Automatically update the application
OK, now
its time to see the results of all this hard work by automatically rolling out
a new version.
1. If the SampleApp version you
deployed to the client isnt running, launch it and leave it running. Remember to use AppStart.exe.
2. Go back to Visual Studio and change
something noticeable on the SampleApp form (ex change the background color).
3. Change the VersionInfo in
AssemblyInfo.cs to be 2.0.0.0.
4. Rebuild.
5. Go to the Web server and create a
2.0.0.0 directory as a peer to the 1.0.0.0 directory. Copy the new version of the application from
the release build directory into the new 2.0.0.0 directory on the Web server.
6. Open UpdateVersion.xml and change
AvailableVersion to be 2.0.0.0. Change
the ApplicationURL to point at the new 2.0.0.0 directory.
7. Save the changes to
UpdateVersion.xml.
As soon as
you save the new UpdateVersion.xml, within 30 seconds the running copy of
SampleApp should detect the newly available version. SampleApp will then download the new version,
apply the update, and pop up the default UI asking the user if they want to
restart and start using the new version immediately. Click Yes in response to this dialog. SampleApp should restart and be running the
new version. If you look at the client
deployment of SampleApp you will notice there is now a 2.0.0.0 directory next
to the original 1.0.0.0 directory. The
1.0.0.0 directory will be cleaned up the next time an update occurs.
On-Demand Install
By
exploiting the extensible nature of the .NET Framework, the .NET Application Updater
component is able to enable another feature, On-Demand installation. By enabling On-Demand installation, only the
main exe needs to be explicitly installed on the client. The remainder of the application can be
automatically downloaded and installed on an as needed basis.
You can see
this feature in action with the SampleApp you just finished building and
deploying. In SampleApp we enabled the
On-Demand install feature by setting the AutoFileLoad property to true. First close SampleApp if it is still
running. Next, in the 2.0.0.0 folder of
the SampleApp client deployment, delete SimpleForm.dll. Youll recall that SimpleForm.dll is the
assembly that contains the form that is displayed when the button on SampleApp
is clicked. With SimpleForm.dll deleted,
you would expect the application to throw an exception when the button is
clicked. Go ahead and try it by running
SampleApp (remember to use AppStart.exe) and click the button in
SampleApp.
What
happened? It worked. In fact if you look in the 2.0.0.0 folder
youll see that SimpleForm.dll has reappeared.
So how did this happen? The CLR
is fundamentally programmable and extensible.
When the CLR could not find the SimpleForm.dll assembly, instead of
simply failing, it raised the AppDomain.AssemblyResolve event. The .NET Application Updater component hooks
that event. The .NET Application Updater
component knows where a valid copy of SimpleForm.dll resides; on the deployment
Web server. It then downloads the
assembly to the local application directory and tells the CLR to try to load
the assembly again. The second load
attempt then succeeds.
On-Demand
installation is enabled and disabled via the AutoFileLoad property on the .NET
Application Updater component. It is a
powerful feature that can make the initial download and install of your
application tiny, but can be difficult to use correctly. You must think hard about where the assembly
boundaries reside in your application and what actions will cause an assembly
to be downloaded. Because the assembly
download involves network I/O, it will take variable lengths of time to
download. During the assembly download,
the application is frozen waiting for the assembly download to complete.
Deployment Security
While the
ability to roll out application updates automatically has great benefits, it
also comes with an inherent danger. When
you make it easier to roll out updates, if not careful, you could also make it
easier to roll out malicious code. There
are two dangers. The first danger is
that someone will spoof the Web server used to deploy updates with their own Web
server. They could then use that Web
server to roll out a virus in place of your application. The easiest way to prevent spoofing or any
other tampering over the wire is to use HTTPS.
To use HTTPS with the .NET Application Updater component, simply use HTTPS
URLs in place of HTTP URLs.
However,
HTTPS is not a silver bullet. There are
two problems with HTTPS. The first is scalability. Using HTTPS requires the server to encrypt
all of the files that are downloaded from the Web server. If an application update is large, the cost
of encrypting the update can overburden the server. The other issue with HTTPS is that it does
nothing to address the second security danger.
The second danger has to do with your Web server being compromised,
either from an insider or from an outsider hacking your server. If your Web server is compromised, its bad
enough, but if it also means that a thousand clients are also compromised via
automatic updates, it could be disastrous.
To solve
this problem, the .NET Application Updater component uses the ability to strong
name a .NET assembly to verify the authenticity of the assembly upon
download. If the .NET Application
Updater detects that an assembly is not signed with your applications key
during download, the update is aborted.
This means that only someone that has the private key of your
application can build updates that can be automatically deployed. .NET security is based on keeping your
private key secret. Even inside your own
organization, only the few select people should have access to your private
key.
To validate
assemblies, the .NET Application Updater component verifies that the public key
on the exe of your current installed application matches the public key on
downloaded updates. The embedded public
key can only be the same if the two assemblies were signed with the same secret
private key. Because the assembly is
loaded by the CLR in order to verify its public key, the CLR does its normal
hash value check to ensure the assembly is in fact a real assembly and has not
been tampered with. If a verification
check fails, it is treated just like any other update failure. To enable verification on download, simply strong
name all of your application assemblies and set the ValidateAssemblies property
on the .NET Application Updater component to True.
Assembly
validation on download works great, but in practice, applications will often
have components signed with different private keys. For example, your application may have two
files: The exe assembly signed with your
private key and a dll assembly that contains a 3rd party charting
control you purchased to use in your application. This 3rd party assembly may be signed
with the 3rd partys private key and not yours. What makes it even more complicated is that
the set of valid private keys that can be used to sign assemblies in your
application could change from version to version. How do you auto-update these types of
applications? To solve this problem,
you create an assembly that contains a list of valid public keys for your
application. Sign this assembly with the
applications main private key (the key that the exe of the application is
signed with) and place this assembly in the directory with the application
update on the Web server. Before the
update download process begins, the .NET Application Updater will check for a
well known assembly AppUpdaterKeys.dll in the application update directory on
the server. If present, the assembly
will be downloaded. The assembly will be
verified against the main application public key. If the signature is valid, the list of keys
will be extracted. From that point on,
any of the keys in that list will be treated as valid signatures for the files
in the update. The zip file that comes
with this paper contains an AppUpdaterKeys project that can be used to build
your own AppUpdaterKeys assembly. The
AppUpdaterKeys.dll also allows you to specify a set of file exceptions. Files specified as exceptions can be
downloaded and updated without passing the validation check. This can be useful when your application
contains non-assembly files, such as pdb files for debugging.
The
recommended approach to security is to use HTTPS URLs to perform update
checks. This provides a first level of
spoofing protection. For update
download, its best not to use HTTPS URLs to avoid the overhead on your Web
server. Instead, strong name your
application assemblies and enable the Validate Assemblies feature.
Extensibility and Terrarium
In the
sample we walked through earlier in this paper we enabled auto-deployment in an
application simply by dropping a component into an application and setting some
properties. While this works well for
many applications, some applications require a higher degree of control that
can only be achieved by writing code.
The .NET Application Updater component has an API that can be used to
customize and replace application update behavior. The .NET Terrarium game (http://www.gotdotnet.com/terrarium/)
I mentioned earlier is a heavy user of these extensibility hooks. Lets take a look at how Terrarium uses the .NET
Application Updater components extensibility mechanisms.
Terrarium
uses a custom XML Web service to check for updates. Terrarium hooks the OnCheckForUpdate event of
the .NET Application Updater component and calls the XML Web service within
this event handler. OnCheckForUpdate
fires on the poller thread. Thus the XML
Web service request is performed on a separate thread from the UI thread and
does not lock the UI. The call to the XML
Web service passes the assembly version of the clients current Terrarium
install. The XML Web service then
decides whether or not a newer update is available for that client. Two values are returned. The first indicates whether or not an update
is available. The second is the URL
where the update resides. For
scalability reasons, in practice, one server is used to check for updates and a
separate server is used to download updates.
On the return from the XML Web service call, if a new update is
available, Terrarium sets the UpdateUrl property of the .NET Application Updater
component to the URL returned from the Web service. This instructs the .NET Application Updater
to use that URL for the next update download.
Finally, Terrarium returns true in the OnCheckForUpdate event handler if
an update is available.
Terrarium
also uses the custom XML Web service to increase scalability. Updates to Terrarium exceed 10 MB in
size. Terrarium also checks for updates
very frequently. Thus, in the beginning,
when a new update was rolled out, it wasnt uncommon to have hundreds of
clients asking for updates at once. This
resulted in Gigabytes worth of download requests and the server couldnt handle
the load. Moving away from using HTTPS
and decreasing the frequency of update checks helped the situation, but the
ultimate solution was to build some intelligence into the XML Web service. When the server is under load, the XML Web service
only tells the first X number of clients that an update is available. All of the rest are told that no new update
is available. The next time the clients
check for updates, the next X set of clients are told an update is
available. In this way, the update is
rolled out in waves and the server is not overloaded.
Terrarium
puts up its own custom UI that integrates better with the overall application. Thus Terrarium disables the default .NET
Application Updater UI. Terrarium hooks
the OnUpdateComplete event. The
OnUpdateComplete event includes a set of information, such as whether or not
the update was successful, what, if anything, caused failure, and an English
string with an explanation of the problem that can be presented to the
user. The OnUpdateComplete event fires
on the main UI thread of the application.
Thus Terrarium raises UI to the user, informing them of update success
or failure, directly in the OnUpdateComplete event handler.
The .NET Application
Updater component also includes additional APIs that allow explicit control
over when actions like update checks and updates are performed. These actions are controllable via the
CheckForUpdate () and ApplyUpdate() methods respectively.
Debugging
The source
code for the .NET Application Updater component is provided so that you can
debug problems and add functionality as needed.
However, before you dive too far into the source code, this section will
spell out some first level debugging options, as well as describe the most
frequently encountered problems by consumers of this component.
The .NET Application
Updater creates a hidden log file named AppUpdate.log that resides in the same
directory as AppStart.exe. All
successful and failed updates are recorded in this log. The log file is especially useful when there
is a particular client machine that wont update successfully. You can use the log to determine when and how
the update failed. In addition, the .NET
Application Updater component uses the Debug class of the .NET Framework to
write out a lot of useful information.
If you run your application in the debugger, you will see this
information in the output window. You
can essentially watch the .NET Application Updater go through its update steps
and see where the problem is occurring.
If for
whatever reason, you just cant get the .NET Application Updater to work, make
sure of the following before you debug too far, more than likely one of these
is the problem you are encountering:
- Do you have IIS Directory
Browsing turned on? If not, the
downloader will see an empty directory on the Web server where the update
resides. The updater will then
download and install an update of zero files, leaving you with the
original unchanged application. See the Setting up the Web server
section earlier in this paper for instructions on how to enable Directory
Browsing.
- Do you have everything deployed
correctly and the correct URLs set?
Its easy to make a mistake here.
Look at the URLs being output by the .NET Application Updater when
running in the debugger. Navigate
to these URLs in the browser and make sure they are valid.
- If your app is installed in the
program files directory, are you an admin or power user on the box? If not, you wont have the write access
necessary to update the application.
- Did you create the AppUpdater
object on the main UI thread of the application? If not, the updater will not be able to
present UI and will fail when firing events back to the UI thread.
- Is the update succeeding, but
the app failing to automatically restart to pick up new updates? The .NET Application Updater component
attempts to restart the application by calling the Application.Exit
method. However, that method is not
guaranteed to shut down an application.
If you spawn & leave separate threads running, it will not
close the process. The solution is
to ensure that all threads are terminated via the Application.OnExit
event, or hook the .NET Application Updaters OnUpdateComplete event and
handle the shutdown yourself.
Summary
Ease of
deployment for client applications was a major goal for the first version of
the .NET Framework. There is no other
technology for building client applications that addresses the deployment
problems as well as the .NET Framework.
Ease of deployment will continue to be a major goal for future versions
of the .NET Framework. The .NET Application
Updater component described here represents some of our thinking in terms of scenarios
we would like to enable directly in future versions of the .NET Framework. However, until that time the .NET Application
Updater component is a great way to get started building auto-updating
applications.