ich client, (or in .NET terminology, “Windows Forms”) applications provide a great user experience in terms of functionality, usability, operating system and inter-application integration. Unfortunately, they have also suffered from a relatively high total cost of ownership compared to browser-based applications, thanks to “DLL Hell”, and a lack of automated upgrade capabilities in the operating system. Consequently, “dumb client” browser applications have become very popular, especially for distributed applications, even though they are generally not as user-friendly as Windows forms applications. But the .NET framework, with features like “Xcopy deployment”, “the end of DLL hell” and “Web deployment” lets developers provide rich-client applications with all their functionality and ease of use, but without the higher cost of ownership.
This article describes the built-in deployment capabilities of the .NET framework?and its shortcomings for rich client applications, and presents a class library that gives you the ability to add automatic upgrade capabilities to your .NET Windows forms applications.
.NET Framework Built-in Capabilities
The .NET framework supports the concept of Xcopy deployment, which is the ability to install a .NET application by simply copying the required files to the target computer. It also supports shadow copying, a process in which .NET copies each assembly into a temporary “shadow copy” directory before opening the assembly (DLL or EXE) file. This mechanism gives you the ability to recompile or to re-deploy new versions of assemblies while the application is in use. The new versions of your assemblies will be used the next time the user starts the application.
In ASP.NET applications, all that happens automatically?the ASP .NET worker process does the work for you. So to update a Web application you can simply copy the updated files to the correct location, and ASP.NET automatically handles shutting down the running application and copying the shadow files. (It’s only that seamless?as long as your application doesn’t use any assemblies requiring registration for COM interoperability, or assemblies that must be registered in the global assembly cache.)
Upgrading Windows Forms applications isn’t quite as simple, partially because client installations are more complex. For example, Xcopy deployment doesn’t create icons in the Window Start menu or on the desktop, nor do applications installed that way appear in the Add/Remove programs control panel applet or take advantage of automatic repair and other Windows Installer features. In addition, Windows Forms applications don’t automatically shadow assemblies; loaded assemblies are locked?you can’t update a running application using Xcopy deployment. Instead, you must update either before the application starts, or update by shutting down the application and then using a different executable program to replace the updated assemblies.
However, you can use shadow copying in your Windows Forms applications by creating an application domain (AppDomain) object and specifying a AppDomainSetup object with the ShadowCopyFiles property set to “true”.
Note that the AppDomainSetup.ShadowCopyFiles property is a String type, not a Boolean, so the following code is incorrect (and doesn’t work).
AppDomainSetup.ShadowCopyFiles = true
Instead, the correct form is:
AppDomainSetup.ShadowCopyFiles = "true"
This technique requires a stub executable, which remains locked during code execution (won’t be shadow copied). For example:
Sub main() ' create a new evidence object, ' based on the current appdomain Dim myDomainSetup = New System.AppDomainSetup() myDomainSetup.ShadowCopyFiles = "true" With AppDomain.CreateDomain( _ "sample", AppDomain.CurrentDomain.Evidence, _ myDomainSetup) .ExecuteAssembly("....FormsinForms.exe") End With End Sub
Setup Projects and Deployment
Visual Studio deployment projects (or third-party tools such as InstallShield, WISE, and others) give you the ability to create an install set that performs a variety of installation tasks. The install set can be deployed to a Web server or file server for easy access, but doing so doesn’t help to keep the client application up to date. However, installing a rich client application this way does provide the benefits missing from Xcopy deployment:
- You can create icons in the start menu or desktop during the install
- The installed application appears in the Add/Remove programs control panel applet, and can take advantage of automatic repair and other Windows Installer features.
You can also deliver applications to clients via the Web. There are two main ways to do that:
1. Launch Applications Using Internet Explorer: The .NET framework provides the ability to deploy your Windows Forms application via a Web browser, by entering the URL to the main application executable. For example:
Launching an application this way allows .NET to find any other assemblies it needs starting from the same location (application base) as the main application. You need to use the .NET Framework Configuration snap-in to configure your security policy so that it trusts code from the location you want to use.
|Author’s Note: When I tested this method, I ran into a few bugs, so keep in mind that some additional testing may be in order. For example, if your assembly name contains a space, as in “http://mydomain.com/my app.exe”, Internet Explorer escapes the space characters automatically, and submits the escaped URL, which looks like http://mydomain.com/my%20app.exe. Unfortunately, the escaped space character causes a file not found exception. I also encountered difficulties using the System.IO.Path methods to load related files, because objects in the System.IO namespace do not appear to have been written for (and don’t work well with) URLs (as opposed to UNC paths). This makes it difficult to write code that can be deployed directly or via the Web depending on circumstances.|
2. Use Assembly.LoadFrom to create instances of objects. Another technique is to create an application stub that uses the Assembly.LoadFrom method to load assemblies from a Web server, for example:
With System.Reflection.Assembly.LoadFrom _ ("http://mycompany.com/myforms.dll") Ctype(Activator.CreateInstance( _ .GetType("Forms.Startup")), Windows.Forms.Form).Show End With
After your “entry” class is loaded, you don’t have to use Assembly.LoadFrom to load subsequent classes?the .NET framework automatically loads instances of new classes from the location you specified for the original class.
Each time you run your application, the .NET framework checks for updates and automatically downloads updated assemblies to the application download cache. To use Assembly.LoadFrom in this manner, your users must always be connected?if they try to run your application while offline, they’ll get an exception. While you can trap the error, I know of no way to force the framework to load the existing assemblies from the application download cache.
Unfortunately, Web-based deployment techniques also have some shortcomings:
- Users are not informed that code is being downloaded. In a wide area network (Internet) scenario, that can mean a long wait if your files are large, with no automatic feedback to the end user.
- Users must start Internet Explorer to launch your application rather than simply clicking a shortcut from the start menu.
- The Web-based deployment mechanisms only handle assemblies and .NET configuration files. Other files associated with your application (such as help files or xml data files), are not upgraded automatically.
- Assemblies loaded from a Web server via either the Assembly.LoadFrom method, or deployed using Internet Explorer run with limited security rights. According to the Deploying .NET applications: Lifecycle guide, “they can only call back to the server from which they were downloaded?a downloaded assembly cannot call a Web service on another computer.”
- In my tests, I found that the System.IO.Path and System.IO.File classes did not work as expected with an HTTP application base directory. In particular, executing the code such as that shown below returned False when BaseDirectory is an HTTP URL, even though the file exists in the specified location.
System.IO.File.Exists( _ System.IO.Path.Combine( _ AppDomain.CurrentDomain.BaseDirectory, _ "mydata.xml"))
Web-based deployment can work well, if:
- Your users are all on the local network and are always connected;
- Your application consists of .NET assemblies only?no help files, script files or any other external files that may need updating;
- You are happy to have your users launch the application from Internet Explorer, or use a stub executable that uses Assembly.LoadFrom to launch your application.
Introducing the AutoUpgrade Class
The AutoUpgrade class provides a framework for adding automatic updates to your rich client application, with just a few lines of code. The AutoUpgrade class was written to provide automatic upgrade support for applications where Web deployment is unsuitable.
To use the code, you reference the AutoUpgrade class library (AutoUpgrade.Lib.dll) from your application, and use its properties and methods to control automatic upgrades. You also need the AutoUpgrade stub program (AutoUpgrade.exe), which runs the automatic upgrade. The stub is required because .NET locks assemblies when they are in use. The StartUpgradeStub method of the AutoUpgrade class invokes the stub automatically from within the IsUpgradeAvailable method when the blnStartUpgrade argument is True.
How It works
The AutoUpgrade class works by comparing the file versions or last modified dates of files on your local file system with data from an XML-based “manifest” file downloaded from a Web server. You can generate the manifest file automatically using the AutoUpgrade class. That creates a list of all of the files in your application, their version numbers (or last modified dates), instructions on how to check for updated versions, and actions to take when a new version of the file is available.
You use the AutoUpgrade framework (encapsulated in AutoUpgrade.Lib.dll) to create the manifest, perform the file version and date comparisons to determine the need for an upgrade, and to perform the upgrade.
Adding Auto-upgrade to Your Application
Here’s the procedure to add AutoUpgrade capability to your application.
1. Add a reference to AutoUpgrade.Lib.dll. In the Visual Studio solution explorer, right-click references, Add reference then click browse and locate AutoUpgrade.Lib.dll.
2. Create a virtual directory. Create a virtual directory on your Web server from which your application will get the new versions of your application’s files. (You will need to change the code sample in step 3 to refer to the virtual directory you are creating). If your application already uses Web services or Active Server Pages, you can use the same virtual directory you use for your application server.
3. Add code to your application to check for new versions of files. Your application needs to create an instance of the AutoUpgrade class and pass in information on the location of the manifest file, call IsUpgradeAvailable and terminate and pass control to the AutoUpgrade stub executable if required. For example:
Sub Main Dim upgAuto As AutoUpgrade() ' In a real application, the path would be ' read from a configuration file or the registry upgAuto = AutoUpgrade.Create _ ("http://mycompany.com/autoupgrade/mymanifest.xml") ' The 'True' argument to the IsUpgradeAvailable ' function tells AutoUpgrade to automatically ' start the upgrade stub if an upgrade is available If not upgAuto.IsUpgradeAvailable(True) Then ' Start the application With new MyApp.MyForm .Show ' Start a message loop. We need this because ' we're starting from a sub Main() System.Windows.Forms.Application.Run() End With Else ' Terminate the application. End If End Sub
4. Compile and deploy your application in a test environment, then update components and re-compile. To test the automatic upgrade, you will need to deploy an “old” version of your application to a test environment, and then recompile your application to update the assembly versions.
|Author’s Note: If you’re using automatic version numbering in Visual Basic .NET, you will have to close Visual Studio and re-load your project to get the version number to update (the C# compiler updates version numbers on each compilation).|
5. Copy an image of your client application to the virtual directory. Make sure the directory structure on the Web server matches that of a deployed application. Create a “client” subdirectory in your virtual directory and copy your files there.
6. Copy AutoUpgrade.exe and AutoUpgrade.Lib.dll to the “client” subdirectory. When the AutoUpgrade framework detects an upgrade, the AutoUpgrade class saves the updated manifest file and executes AutoUpgrade.exe, which automatically downloads the updated files from your source location to the upgrade cache directory, then replaces your application files You should place the files AutoUpgrade.exe and AutoUpgrade.Lib.dll in your “client” subdirectory.
7. Create a manifest file for your “new” version. Initially you can create a manifest file automatically by navigating to your virtual directory path from the command prompt and executing the command:
AutoUpgrade [path] [target]
In the preceding method call, [path] is the path to search for files, and [target] is the file name where you want to save the manifest file. For example (from your virtual directory’s root directory):
clientAutoUpgrade client mymanifest.xml
This syntax invokes the GenerateManifest() method, which writes entries for all files that default to the copyFile action. You can set additional options by editing the manifest file in an XML editor or notepad.
After completing these steps, run your application from the location you specified in step 4. AutoUpgrade will check the entries in the manifest file against your current installation, and will launch an automatic upgrade.
For “live” installations, your server setup could automatically:
- Create the virtual directory creation using ADSI (refer to the .NET framework documentation on the System.DirectoryServices namespace).
- Install an “image” of your client application in the virtual directory.
- Automatically generate a manifest file, or install one you prepare as part of your build process.
The Installer Class
.NET applications can inherit the System.Configuration.Install.Installer class to perform custom actions at installation time (in a Visual Studio deployment project, you can create custom actions to run instances of the Installer class at install time).
AutoUpgrade calls all instances of the Installer class marked with the RunInstaller(true) attribute by using the AssemblyInstaller object in the System.Configuration.Install namespace.
About the AutoUpgrade Class Framework
The AutoUpgrade classes were created with flexibility in mind, so all the methods and properties are public.
You can create a minimal implementation of the AutoUpgrade class by simply calling Create, followed by a call to IsUpgradeAvailable (as shown above), or you can completely control the upgrade process from within your application by calling IsUpgradeAvailable(false) and reading and writing to the UpgradeFiles list.
The code download contains a Word file (Class specification.doc) that has a full description of the AutoUpgrade properties, methods and events, so I won’t show them all here, but you can see the key methods and properties in the sidebar Key AutoUpgrade Methods and Properties.
Adapting AutoUpgrade for Different Deployment Scenarios
There are several ways you can adapt the AutoUpgrade framework for various deployment scenarios, such as.
- Use a Web service to automatically generate the manifest. The AutoUpgrade class can be used to automatically generate a manifest, using the contents of a subdirectory. The code sample (AutoUpgrade.Web) shows how to provide a Web service that one of the overloaded Create methods can access to generate a manifest file “on the fly”.
- Use AutoUpgrade to do a user-driven upgrade check, or create a program to check for an upgrade periodically. The code provided in the section “Adding auto-upgrade to your to your application” (above) can be used just as easily to provide a user-driven upgrade, or to check for an upgrade every few days, instead of every time the application starts. You must terminate the application to release file locks before upgrading if you’re using the AutoUpgrade.EXE stub.
- Deploy an application “bootstrap” to clients that automatically downloads the full application. You could use the AutoUpgrade class and a small bootstrap program, or the AutoUpgrade.exe upgrade stub with a modified manifest file (with the “SourcePath” set correctly) to deploy your application to end-users. The AutoUpgrade exe, AutoUpgrade.dll and a manifest file are small enough to send by email. You could even add an Installer class to one of your key assemblies to create start menu icons.
AutoUpgrade does not automatically handle COM registration or installation to the global assembly cache. If you need to perform such special installation functions, you must add an Installer class to your assembly as described above and add the code to perform the special actions.
AutoUpgrade Reduces Costs
The AutoUpgrade class framework was created with a couple of scenarios in mind:
- Software vendors or IT departments developing single-user applications who want to provide an automatic upgrade facility to end-users.
- Software vendors or IT departments developing multi-user applications who create a “server setup” to push new client versions automatically when the server software is upgraded.
Adding automatic upgrade capability can reduce the cost of ownership for your clients by bringing the ease-of-deployment of Web-based applications to your Windows forms applications. By making it easy for end-users to have the latest version installed, you can reduce support calls (ever had a call for a problem you fixed months ago?), and provide greater end-user satisfaction by “pushing” upgrades that fix problems before users even encounter them.
For most applications, using the procedure listed in the Adding Auto-upgrade to Your Application section of this article, you can add AutoUpgrade functionality in less than an hour. The classes are flexible enough that you should be able to adapt them to your particular scenario with ease.