Share via


BITS

Write Auto-Updating Apps with .NET and the Background Intelligent Transfer Service API

Jason Clark

Code download available at:BITS.exe(363 KB)

This article assumes you're familiar with C# and Visual Basic .NET

Level of Difficulty123

SUMMARY

Both the .NET Framework and Windows have some very interesting APIs for creating applications that are capable of updating themselves automatically over a network. There are many advantages to writing your application to update itself like Windows Update does, including convenience for your users, from easier maintenance to network administration. Automatic updates require attention to factors such as discovery, security, and file replacement. In this article, the author covers the BITS API as well as a number of features of the .NET Framework that take care of these facets of auto-updating using the same facilities that the Windows Update uses.

Contents

The Challenge
BITS Basics
BITS, COM, and Managed Code
Using BITS with Auto-updating Apps
Some BITS Considerations
The .NET Framework and Security
Strong Names and Secure Binding
Update Packaging and Replacement
File Replacement and Extraction
The AutoUpdateApp.exe Sample App
The Future of Auto-updating Apps

I have to admit, I love the Windows® Update feature. My computer is connected to the Internet about 85 percent of the time that it is turned on and yet, like most people, I certainly don't use the network that much. Windows XP takes advantage of this unused bandwidth by comparing the most recent service packs and hotfixes available online with those installed on my machine. If it finds I need updates, it downloads them in the background. Once completed, Windows notifies me of the arrival of new bits that need to be installed.

If I had my choice, every application on the client would allow auto-updating like Windows does. There are many benefits and the connectivity is in place now to make this a viable feature. If you're going to make your applications update themselves automatically, you'll have to write your code to handle discovery, downloading, security, and replacement.

For handling the actual downloading, I will cover a new feature of Windows called the Background Intelligent Transfer Service (BITS). Following that discussion, I will talk about features of the .NET Framework that let you solve the security and replacement problems for auto-updating applications.

Note that while the BITS 1.5 redistributable works fine on Windows 2000 and Windows XP, Microsoft does not plan to support the BITS API on Windows 9x or Windows Me. BITS is expected to ship as a component of future versions of Windows.

Before moving on, however, it is important that I point out that your application need not be managed under .NET to take advantage of the technologies covered in this article. BITS is part of the operating system itself, and the .NET Framework technologies covered here can be used for the auto-updating feature of your otherwise unmanaged application.

The Challenge

In order to find updates on a remote server, your application needs some means of querying the network. This requires networking code, as well as a simple protocol for the application and server to communicate over. I will come back to the topic of discovery later in this article.

Next you have to be able to download the bits. Downloading may not seem like much of a problem considering how simple networking is with .NET, but it's one thing to download files a user requested; it's another thing to download large files without the user's consent. The polite auto-updating application will download updates using only leftover bandwidth. This sounds great, but as you can see it poses a fairly difficult technical problem. Fortunately, a solution already exists.

Security is probably the most critical consideration. Think about the Windows Update feature for a moment. One of its primary purposes is to get security fixes. Imagine if Windows Update itself failed to verify that it was installing safe code. Obviously, any application that downloads code from the Internet and executes it must make security a top priority. Therefore, I will discuss how to make your auto-updating application secure.

The final consideration is the process of replacing your application with a new version of itself. This problem is an interesting one because it requires code to be able to run while removing itself from the system. There are a number of ways to manage this trick.

The great thing about these challenges is that between the .NET Framework and Windows itself, all the tools are in place right now to solve these problems.

BITS Basics

BITS is a cool new file transfer feature of Windows that asynchronously downloads files from a remote server over HTTP. BITS can manage multiple downloads from multiple users while making use of idle bandwidth exclusively. Although the use of BITS is not limited to auto-updating applications, it is the underlying API used by Windows Update. And since it is available to any application, it can be used to do much of the really tough work involved in creating an auto-updating application.

Here is the basic idea. An application asks BITS to manage the download of a file or set of files. BITS adds the job to its queue and associates the job with the user context under which the application is running. As long as the user is logged on, BITS will drizzle the files across the network using idle bandwidth. In fact, the code-name for the BITS technology is Drizzle, which, it turns out, is quite descriptive of what BITS does.

How does all of this work? The technology is actually fairly sophisticated. First, BITS is implemented as a Windows service that maintains a collection of jobs organized into a set of priority queues: foreground, high, normal, and low. Each job in the same priority level is given bandwidth via time slices of about five minutes, in a round-robin fashion. Once there are no jobs remaining in a queue, the next priority queue is inspected for jobs.

Jobs in the foreground queue use as much network bandwidth as they can, and for this reason the foreground priority should only be used by code that is responding to a user request. The remaining priorities—high, normal, and low—are much more interesting because they are all background priorities, which is to say that they only make use of network bandwidth that's not in use.

To achieve this background feature, BITS monitors network packets and disregards packets that it recognizes as its own. The remaining packets are considered the active load on the machine's bandwidth. BITS uses the active load information along with the connection speed and some other statistics to decide whether it should continue downloading files or back off in order to increase throughput for the active user. Because of this, the user doesn't experience bandwidth problems.

The ability to drop what it is doing at a moment's notice is very important for BITS. In many cases, only part of a file is downloaded before BITS must give up the network or even lose connection altogether. The partially downloaded file is saved, however, and when BITS gets another crack at the network, it picks up where it left off. This ability to recover does have some side effects.

Remember that BITS is used to transfer files from HTTP servers. A server should be HTTP 1.1-compliant or at least support the Range header in the GET method for BITS to work. This is because BITS needs to be able to request a portion of a file. In addition, the content being downloaded must be static content such as a markup file, code file, bitmap, or sound. A GET request including a Range header makes no sense when requesting dynamic content such as that produced by CGI, ISAPI, or ASP.NET.

Currently, there are two versions of BITS: 1.0 and 1.5. BITS 1.0 ships with Windows XP and has the following features: interruptible background downloading of files, download prioritization, optional notification of completed jobs and error situations, and optional progress notifications for use with dialog boxes and other UI elements. BITS 1.5 ships with Windows .NET Server. In addition to the features contained in BITS 1.0, version 1.5 has interruptible background uploading of files and authenticated connections using Basic, Digest, NTLM, Negotiate (Kerberos) or Passport. BITS 1.5 is available as a redistributable that is compatible with Windows 2000 and greater (see Background Intelligent Transfer Service).

The BITS 1.0 feature set is sufficient for writing auto-updating applications, but the BITS 1.5 features allow you to perform more complicated tasks like selling updates or handling interaction between the application and the server.

BITS, COM, and Managed Code

The BITS API is implemented as COM objects, and as of this writing a .NET Framework version of the API is not available. Fortunately, the BITS API is very straightforward and simple to use. The sample application included with this article is written in C#, so to use the BITS API I did have to mess around with the COM Interop features of the .NET Framework. Without going into too much detail about runtime callable wrappers (RCW) and such, I'll show you the process I followed to use the API.

If I had been using C++, my BITS code might have started out something like this:

IBackgroundCopyManager* pBCM = NULL; hr = CoCreateInstance(__uuidof(BackgroundCopyManager), NULL, CLSCTX_LOCAL_SERVER, __uuidof(IBackgroundCopyManager), (void**) &pBCM); if (SUCCEEDED(hr)) { // Party-on with the pBCM interface pointer }

The C# equivalent creates the BackgroundCopyManager object using the new keyword and then gets a reference to the IBackgroundCopyManager interface through casting, rather than a call to a method such as CoCreateInstance or QueryInterface. The following code snippet is a C# example to acquire the IBackgroundCopyManager interface:

IBackgroundCopyManager bcm = null; // Create BITS object bcm = (IBackgroundCopyManager)new BackgroundCopyManager();

The simplicity of this code is somewhat misleading because more work needs to be done to associate the managed BackgroundCopyManager and IBackgroundCopyManager types with the underlying COM object and interface, respectively. The .NET Framework manages interop with COM objects through an RCW. To associate a managed type with an RCW, you must use attributes. The code in Figure 1 shows how to declare the BackgroundCopyManager class and IBackgroundCopyManager interface so that they represent the BITS COM objects.

Figure 1 Defining RCW Types

[GuidAttribute("4991D34B-80A1-4291-83B6-3328366B9097")] [ClassInterfaceAttribute(ClassInterfaceType.None)] [ComImportAttribute()] class BackgroundCopyManager{} [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] [GuidAttribute("5CE34C0D-0DC9-4C1F-897C-DAA1B78CEE7C")] [ComImportAttribute()] interface IBackgroundCopyManager { void CreateJob( [MarshalAs(UnmanagedType.LPWStr)] string DisplayName, BG_JOB_TYPE Type, out Guid pJobId, [MarshalAs(UnmanagedType.Interface)] out IBackgroundCopyJob ppJob); void GetJob(ref Guid jobID, [MarshalAs(UnmanagedType.Interface)] out IBackgroundCopyJob ppJob); void EnumJobs(uint dwFlags, [MarshalAs(UnmanagedType.Interface)] out IEnumBackgroundCopyJobs ppenum); void GetErrorDescription( [MarshalAs(UnmanagedType.Error)] int hResult, uint LanguageId, [MarshalAs(UnmanagedType.LPWStr)] out string pErrorDescription); }

The code in Figure 1 is so riddled with attributes (ComImportAttribute, GuidAttribute, MarshalAsAttribute, and so on) that the real code seems obscured. In point of fact, there is no real code. The interface and class definition are little more than a marshaling placeholder in the form of metadata that the common language runtime (CLR) uses to call through to the BITS COM API.

The InteropBits.cs file included with the code sample (see the link at the top of this article) provides C# Interop code for all of the interfaces, enumerated types, and structures used with the BITS API. I included the full implementation even though the sample only makes use of a few methods because you'll find it useful if you decide to explore more features of the API. It is worth noting that the code in the InteropBits.cs file is not a managed API, but is rather an Interop API over the COM API implementation. Eventually Microsoft is expected to release an API that exposes BITS functionality to .NET Framework code in a way that is consistent with the rest of the .NET Framework Class Library.

The code in Figure 1, as well as the complete implementation in the InteropBits.cs sample file, was created by hand. The .NET Framework SDK ships with a tool called TlbImp.exe which will create and compile similar code for you into a managed assembly, if you have a TLB file describing the COM API that you are targeting. The BITS API does not ship with a TLB file; however, the Platform SDK does include an interface definition file called Bits.idl which describes the interfaces.

I used the MIDL.exe tool (which also ships with the Platform SDK) to create a TLB file from Bits.idl. I then used TlbImp.exe to create a managed assembly representing the TLB file. I used ILDasm.exe to disassemble the assembly produced by TlbImp.exe into intermediate language. Finally, I produced C# code using the intermediate language as a guideline, adjusting it in places to make it more usable and correct. This approach to COM Interop with C# is admittedly tedious, but it offers an exceptional degree of control over the eventual outcome.

Using BITS with Auto-updating Apps

The BITS service manages file downloads in terms of jobs. An application creates a transfer job and then adds one or more files to the job. Once the job's file list is established, the job is resumed because jobs start their lives in a suspended state. Jobs are used to manage details such as priority, authentication, and error management. At any time, a job can be cancelled by the application.

Once BITS has finished transferring all the files in a job, the application completes the job by calling a method. The Complete method copies all of the files to their final destinations. Although the BITS documentation refers to the job completion as "copying" files, in reality temporary hidden files are created in the destination directory, and the Complete method simply renames the hidden files and makes them viewable.

Even minimal use of the BITS API (using only the IBackgroundCopyManager and IBackgroundCopyJob interfaces) can achieve everything you need for an auto-updating application. If you need features such as job enumeration, or completion and error notification, then you will have to use additional interfaces.

The sample application, AutoUpdater.exe, uses BITS to achieve both update discovery and download. For this to work, the application defines a pattern for the successive names of updates: I chose Update1.dll, Update2.dll, and so on for the sample. Although this is by no means the only way of discovering updates, it works well with the BITS functionality. The sample application maintains an XML file for the purpose of storing update state. The two pertinent pieces of information in the file are the next update number and a GUID describing the current BITS download job, if there is one in progress.

Each time the application runs, it opens the XML file and checks to see if the next update has already been downloaded. If not, and there is no job GUID in the XML file, the application initiates a BITS job to download the next update in line. The following code shows the methods necessary to initiate a download job:

IBackgroundCopyJob job=null; // Create a bits job to download the next expected update bcm.CreateJob("Application Update", BG_JOB_TYPE.BG_JOB_TYPE_DOWNLOAD, out jobID, out job); // Add the file to the job job.AddFile(updateUrl, localLocation); job.Resume(); // start the job in action

The call to job.AddFile passes the location of the file to be downloaded and the complete local path and file name where the final file should be stored upon job completion. Notice that the job starts in a suspended state and must be resumed before the BITS service will do any work with the job.

If there is a GUID in the XML file, then a BITS job has already been initiated by a previous launch of the auto-updating application. So the GUID is passed to the IBackgroundCopyManager.GetJob method to see if the previously arranged download job has produced results. The next step depends on the state of the job.

As you can see in Figure 2, if the job is in the error state, then the application completes the job (which clears the failed job from the queue) and then continues to create a new job for the same update file. The most likely reason for this error state is simply that an update with that name does not yet exist. This completes the discovery part of the sample application.

Figure 2 Checking Job State and Completing Jobs

bcm.GetJob(ref jobID, out job); // Get the BITS job object job.GetState(out state); // check its state switch(state){ case BG_JOB_STATE.BG_JOB_STATE_ERROR: // If it is an error job.Complete(); xml.BitsJob = Guid.Empty; Marshal.ReleaseComObject(job); job = null; break; // continue on to create a new job case BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED: // If we got the file job.Complete(); // then complete job xml.BitsJob = Guid.Empty; return; // all done, return default: return; } ... // Initiate a new job for update download

The great thing about using BITS to poll for updates is that BITS does its work in the background. So even though polling is not particularly elegant, the nonobtrusive nature of a BITS download makes it a very viable means of discovering updates.

If the job is in the transferred state, then the application completes the job by calling IBackgroundCopyJob.Complete. This causes the file to be put in the destination directory, making it ready for updating. Then the application returns because, at this point, the download piece of the puzzle has been solved.

Finally, in the default case in Figure 2 the sample application simply returns and ignores the job. The job could be in one of two states: the transferring state (which means that a job is in progress) or the transient error state. Both cases are considered to be potential success cases, so the application just leaves well enough alone. If a BITS job has been put in a transient error state, the BITS service thinks that the error may be recoverable, so it will try again. Eventually, a job that has been in transient error will either succeed or BITS puts the job in the error state.

Amazingly enough, aside from error recovery logic, this covers the whole discovery and downloading component of the AutoUpdater.exe sample application. Considering the advanced functionality of the download process, the implementation looks almost nothing like network communication and has more in common with a file copy. It is likely, however, that in a production application you will find a variety of uses for other features of the BITS API such as automatic transfer notifications, job priority manipulation, and upload jobs.

Some BITS Considerations

BITS is designed to never initiate a network connection. This is best because you don't want your non-connected application dialing up an ISP just to poll for an update. However, BITS 1.0 and 1.5 both suffer from a limitation with Internet connection sharing. If system A is sharing system B's Internet connection, an app that initiates a BITS job on system A may cause system B to initiate dial-up. This is because the current implementation of the BITS service does not take connection sharing into consideration.

This is a considerably tough problem to solve, but a fix may come with a future revision of BITS. The Windows Update feature is subject to this same problem. Your application that uses BITS will behave no differently than Windows does in terms of initiating dial-up on a shared connection.

A final concern with BITS is job file consistency. If your download job includes multiple files, BITS does not consider the job finished nor do the files arrive at their final destination until all the files are available. However, BITS has no way of knowing if a file on the server changes after other files in the job have already been downloaded. For this reason, updates made on the server can affect download consistency on the client.

There are two ways to manage this issue. The first is to limit BITS downloads to single-file jobs, which is the approach I take in the sample application. The second approach is to store all of the files for a single job in a single directory on the server. When an update is made to the files, create a new directory for the new set of files on the server. This way, clients that are in mid-transfer on an older job will not be affected by the new set of files. The problem with this solution is that the location of server-side files changes and the client needs a way to know where to find the new files.

When you find yourself needing to troubleshoot BITS code, there is no better helper than BitsAdmin.exe. This tool ships in the Support\Tools subdirectory of the Windows XP CD. To save time, rather than running the tools installation, I manually extracted the .exe from the Support.cab file.

The BitsAdmin.exe tool is very useful when you are first becoming familiar with BITS downloads. Nearly every feature of the API is exposed through switches on this command-line tool. You can also enumerate jobs, check job status, and view verbose job and error information. You can initiate, suspend, resume, and complete download jobs. Finally, you can change job priority and cancel jobs from BitsAdmin.exe.

It is rare that a Windows API is so accessible through a companion tool. In fact, scripts such as batch files can make complete use of BITS through the BitsAdmin.exe tool. BITS is a great solution for discovery and downloading updates, and for most applications the drawbacks are minimal. Now let's look at security.

The .NET Framework and Security

The .NET Framework introduces some very interesting security constructs for use in code. Code Access Security is one example. Another piece of security infrastructure is strong-name binding to managed assemblies. I chose to use this feature for more secure transfer of code updates from the server to the client. The problem here is that an auto-updating application is contacting a server across the Internet and downloading files, including code that it will install on the client. These new files must not be able to be manipulated by a malicious party somewhere on the wire between the client and the server.

One fix to this problem would be to conduct the entire exchange of data over an HTTPS connection. The BITS service is capable of targeting resources via the HTTPS protocol. HTTPS is just HTTP communication that occurs in an encrypted SSL/TLS channel. There are several drawbacks that are associated with this solution, however. The first is decreased performance.

Web servers excel at sending static content such as an HTML document or an EXE file. When Microsoft Internet Information Services (IIS) receives a request for a static file, it can make a single call to the TransmitFile Win32® API. TransmitFile copies the bits verbatim from the file system into the network stream at the driver level, providing excellent performance. The moment you introduce encryption through HTTPS, however, performance is drastically reduced because each byte of the file must be encrypted. This can increase the cost associated with update servers.

Another problem with using HTTPS is that it makes the Web server a very vulnerable node in the security infrastructure. If the Web server is compromised, then clients will blindly download and install the malicious content installed on the server.

Using a reasonably simple feature of the .NET Framework, however, you can achieve more secure transfer of update files without either of these drawbacks. First, an encrypted SSL/TLS channel is not necessary (so your Web server's throughput will not be affected). Second, with this solution, the big security threat from a compromised server is a denial of service to the client; the server will be prevented from running the malicious code on the client. This feature of the .NET Framework is known as strong-name binding and is part of the assembly loader.

Strong Names and Secure Binding

One of the longtime problems with DLLs which the .NET Framework has alleviated is the weak naming scheme of using only a file name to identify reusable code. This has led to all sorts of application problems that are collectively rolled into the term "DLL Hell." The solution is a strong name.

A strong name consists of four pieces of information: file name, version number, culture information, and a public key. The .NET Framework uses the strong name to enforce more strict binding between components. The framework also uses the public/private key pair to validate strongly named assemblies before loading. In the case of a strongly named assembly, the .NET Framework language compiler (C# or Visual Basic® .NET) builds the public key right into the DLL file. In addition to the public key, the compiler also hashes the contents of the file and encrypts it using the private key (which is tightly held by the build organization).

An encrypted hash is also known as a digital signature. To check the digital signature, the public key is extracted from the assembly file and used to decrypt the hash. Then the contents of the file are rehashed and the results are compared with the signature hash. If the contents of the assembly have changed in any way, the signature verification will fail because the hasher won't match.

If you guessed that using the public key in the assembly to confirm the very same assembly doesn't sound very secure, you're correct. However, if the code doing the verification had a way of making sure that the public key was a well known or expected public key, then you would have a more complete security solution. The .NET Framework does this by using a hash of the public key known as a public key token or originator.

When loading an assembly into your application, you can specify a public key token. The .NET Framework will only load the assembly if it is strongly named and if the public key of the strong name matches the public key token passed to the Assembly.Load method.

Now I'll walk through the process of strongly naming an assembly and loading it using an originator. The following C# code can be used to build a strongly named assembly. The assembly won't be able to do anything but demonstrate a strong name binding:

using System.Reflection; [assembly:AssemblyKeyFile(@"Keys.snk")]

Before this code can be built, you must create the key file (Keys.snk) that contains the public/private key pair. You do this using the Sn.exe tool that ships with the .NET Framework SDK from the command line as follows:

>sn -k Keys.snk

Now you can compile the strongly named assembly into a .NET Framework assembly DLL using the C# compiler as follows:

>csc -t:library StrongName.cs

The resulting assembly will be named StrongName.dll. To discover the public key token that results from this strong name, you can use Sn.exe with the /T switch, as shown in Figure 3.

Figure 3 Extracting an Assembly's Public Key Token

Figure 3** Extracting an Assembly's Public Key Token **

The example in Figure 3 shows a DLL whose public key token is d586e46fd39d13b5 in hexadecimal. To load this assembly with strong name verification, all you need is the following code:

// Load the update String name = Path.GetFileNameWithoutExtension(patchName); name = "StrongName.dll, PublicKeyToken=d586e46fd39d13b5"); Assembly update = Assembly.Load(name);

This code either verifies the signature and returns a reference to the assembly object or it fails the verification and the Assembly.Load method throws the FileLoadException. Either way, using Assembly.Load, the code is able to verify that the assembly has not been modified in the least since it was built.

An auto-updating application can download an update file using BITS without concern for server-side security. Then, before the update is installed and used on the client, the assembly can be verified using Assembly.Load. The only prerequisite is that the original application is installed with existing knowledge of a public/private key pair in terms of the public key token. Any future updates that are built using the matching key-pair can be verified on the client before they are installed and used. Meanwhile, the key-pair file, used for building updates, can be stored on a machine that is not even connected to a network.

This security mechanism is great, but it does raise a couple of questions. For example, can you update non-code files such as bitmaps and data files? Do you have to strong-name build all of the DLL and EXE files to use this mechanism? The answers to these questions are yes and no, respectively.

Update Packaging and Replacement

An auto-update solution wouldn't be that interesting to me if it didn't work for non-code files, or if it required all of my DLL and EXE files to be managed and strongly named. So I needed to come up with a way to use .NET Framework strong-named binding without constraining updates to .NET Framework assembly files. The answer is to embed the update files into an assembly file as assembly resources. This not only allows any file type to be protected by strong naming, but it also simplifies BITS job creation by making all update jobs single-file downloads. One big file is really a package of many files. In fact, the package could include CAB files or even MSI scripts—the possibilities are endless.

All of the .NET Framework language compilers let you embed arbitrary files into an assembly file. I will show you how to do it with the C# compiler, but the concept applies as easily to Visual Basic .NET or managed C++. The following command line will build the files Code.dll, Bitmap.gif, and Data.xml into an Update2.dll that includes each file as an embedded resource:

csc /res:Code.dll /res:Bitmap.gif /res:Data.xml /out:Update2.dll /t:library

Note that the C# compiler is flexible enough to build a DLL assembly without a .cs code file. The DLL contains nothing but embedded resources. However, if you want your DLL to include a strong name, you should also include a short .cs file that includes code similar to StrongName.cs.

The code necessary to discover embedded resources and copy them back into the file system is shown in Figure 4. The code makes use of the Assembly.GetManifestResourceName and Assembly.GetManifestResourceStream methods to find resources and copy bytes from them into files in the file system. The code in Figure 4 does not preserve the dates of the original files in the file system, but could be easily modified to do so. Note that the extraction process would only take place after Assembly.Load has verified the signature of the container assembly.

Figure 4 Extracting Assembly Resources

Assembly update = Assembly.Load(...); // Load assembly // Get resource names String[] resources = update.GetManifestResourceNames(); // Enumerate resources foreach(String s in resources){ using( // Get stream the resource stream Stream res = update.GetManifestResourceStream(s), file = new FileStream(s, FileMode.CreateNew) // Create file ){ Int32 pseudoByte; while((pseudoByte = res.ReadByte())!=-1){ // Copy the bytes file.WriteByte((Byte)pseudoByte); } } }

File Replacement and Extraction

Extracting files into your application directory during an application update is a surprisingly nontrivial problem, even with strong names and assembly resources. The crux of the problem is that your application is executing while it is copying over its own files. To further complicate matters, the application you're running might be something like Notepad. With such applications, it is common for multiple instances to be running at the same time.

When you have such an application, a one-size-fits-all solution is nearly impossible. Rather than prescribing a single solution, it is best to look at the features that are available for managing the replacement problem. First, a simple possibility is to run your auto-update feature in a separate process from your main application. This is a particularly enticing approach when your application is unmanaged and you want to extend it to use the technologies covered in this article. The update process could handle all of the BITS logic as well as file extraction. The update process could even wait until all instances of the main application had terminated before attempting a file extraction.

There are three main disadvantages of running the extraction code in a separate process. First, it may make it difficult to patch the auto-update code itself. Second, this approach is less advantageous on the server side because the server is unlikely to shut down. Third, if your main process is managed by a Windows Job object (not to be confused with a BITS job), then when the main process exits, the auto-update process will be automatically terminated by Windows as well.

The AutoUpdateApp.exe sample application runs the auto-update logic in the same process as your main application logic. If you choose to do the same, you will run into the problem of deleting and replacing DLL and EXE files that are in use by the process. One little-publicized trick for deleting a DLL or EXE that is in use is to rename the file to a temporary file name, and then copy the new file over the old name. You can then use the MoveFileEx Win32 API to log the file for deletion when you reboot the system, or include logic in your application that will search for each of the temporary files and delete them.

Even when using the rename trick, you still have to consider the possibility that while an update is in progress the user will run another instance of your application and it will load with half of the old files and half of the new files. There are ways to address this possibility. One is to use the shadow copy feature of application domains in the CLR.

The CLR runs managed code in a logical application container called an application domain or AppDomain. Multiple AppDomains can run in a single Windows process. When an AppDomain is created, it is possible to mark the new domain to shadow-copy files. What this means is that as your application loads assemblies, the assembly files are copied into a hidden temporary directory and loaded from the shadowed location. This feature is used by the ASP.NET classes in the .NET Framework to get application files out of the way so that they can be updated by the ASP.NET code generator.

AppDomains are also useful for application reexecution. Ideally when an update is available, your application will prompt the user to see if they are interested in applying the update. After they say yes, the application should just run with the new features in place. For some applications, it is acceptable to relaunch the updated application in a new process. In other cases, it is preferable that the new code begin working right away in-process with the EXE that performed the update.

It is easy to use AppDomains for reexecution of a managed EXE. An example of code that does this can be found in the RelaunchExe method in the sample code included with this article (see the link at the top of this article). The RelaunchExe method creates a new AppDomain and then reexecutes the executable, passing it the same command-line arguments that were used when launching the application in the first place.

The AutoUpdateApp.exe Sample App

The code sample for this article not only includes auto-update logic, but also builds two updated versions of the application so that you can see the whole solution in action. Let's take a look at it now.

Even though Visual Studio® .NET is a powerful coding environment, there are times when you just have to create your own batch builds. My project is one example, a multiversioned build that was difficult to generate as a Visual Studio project. So I am publishing the sample as a collection of source files and a batch file to perform the build from the command line.

To try out the application, download the archive from the link at the top of this article and extract it into an empty directory. Then run Build.bat from the command line. For the batch file to succeed, it is necessary that csc.exe (the C# command-line compiler) and sn.exe (the strong-name utility) are both on the path environment variable. You will find csc.exe in the .NET Framework install in the following directory: C:\Windows\Microsoft.Net\Framework\. You will find the Sn.exe tool in the .NET Framework SDK directory which typically installs at one of the following two locations: C:\Program Files\Microsoft.NET or C:\Program Files\Microsoft Visual Studio .NET.

Once the project has been built using Build.bat, two important directories have been created: app and updates. Note that an sln file should appear so that you can easily edit the code files using Visual Studio .NET (you should continue to build the project using the batch file, however).

The first directory, app, contains the application install. Change the directory to the app directory and run AutoUpdateApp.exe to try it out. The actual functionality of the application is to display the first bitmap it finds in its launch directory (see Figure 5).

Figure 5 AutoUpdate Simple App

Figure 5** AutoUpdate Simple App **

To see the update functionality in action, create an IIS virtual root directory called updates that grants anonymous download access. The goal is to make https://localhost/updates a working location for posting all of your updates. Then copy the contents of the second directory, updates, into your virtual root directory.

Assuming everything is set up properly, your application should now be able to find Update1.dll and Update2.dll at https://localhost/updates. From the command line, execute and shut down AutoUpdateApp.exe a couple of times. On about the second or third launch, the application should notify you that its first update is available to be applied. The first update just adds new GIF files to the directory. The second update actually installs a new copy of the AutoUpdateApp.exe, which will now have a feature that runs a slide show of all the GIF files in the launch directory.

The first time you launch AutoUpdateApp.exe the application creates a data file named UpdateState.xml. This file includes information that controls the functionality of the AutoUpdateApp.exe, including the network location where the application looks for updates. If you don't want to use a location on https://localhost/, you can change the URL in UpdateState.xml to use a different update server.

The Future of Auto-updating Apps

Today there are some great features available for writing automatically updating applications. That said, Microsoft developers are currently hard at work making auto-updating an even more simple and natural part of application development. Version 1.0 of the .NET Framework already provides some basic native functionality here with its no-touch deployment feature (see .NET Zero Deployment: Security and Versioning Models in the Windows Forms Engine Help You Create and Deploy Smart Clients). Ultimately, the easier it is for users to install and update the applications you write, the more successful your applications will be.

There are plans to introduce built-in services for update discovery and replacement in a future release of Windows. The BITS service will continue to be the underlying download mechanism, but additional API abstractions will be in place to simplify download and security. In addition to this, BITS will be extended to include an easy to use UI for managing application updates.

I look forward to the day when the line between launching and installing an application will be entirely blurred. But at least today I can write applications that, once installed, will update themselves. That's a pretty reasonable first step.

For related articles see:
Using Windows XP Background Intelligent Transfer Service (BITS) with Visual Studio .NET
.NET Interop: Get Ready for Microsoft .NET by Using Wrappers to Interact with COM-based Applications
Applied Microsoft .NET Framework Programming by Jeffrey Richter (Microsoft Press, 2002)

For background information see:
.NET and COM: The Complete Interoperability Guide by Adam Nathan (SAMS, 2002)

Jason Clarkprovides training and consulting for Microsoft and Wintellect (https://www.Wintellect.com) and is a former developer on the Windows NT and Windows 2000 Server team. He is the coauthor of Programming Server-Side Applications for Microsoft Windows 2000 (Microsoft Press, 2000). You can get in touch with Jason at JClark@Wintellect.com.