.NET provides new tools to make deployment of fat client .NET applications easier.
This article describes the basics of .NET Auto-Deployment technology and the security mechanism that prevents users from inadvertently running code distributed by hackers and virus writers.
.NET Automatic Deployment is a feature built into the .NET Framework allowing applications to download assemblies (via HTTP) from remote locations on an as-needed basis.
With this feature, you can post your fat client application to a Web server and be sure that users automatically use the latest version the next time they run the application. Imagine that: No more CDs to ship with updates, and no more support calls from users that are a few versions behind!
The .NET Framework provides a lot of the functionality required for automatic deployment out-of-the-box. For example, .NET knows how to download an assembly from a remote location, cache it in the user's disk, and download it again only when a new version is available in the remote location. All these features come built into the .NET Framework.
Auto-Deploying an Executable
There are two pieces to auto-deploying a Windows Forms .NET application. One piece lets your users have access to the main executable. The other piece lets your users download the rest of the application as they use it.
.NET applications know how to download code from a remote location, when to download it, and how to cache it for future reference.
Let's talk about the first piece. Giving users access to the main executable is fairly easy to do with .NET. You just need to post your executable to a Web server and let the users know the URL.
For example, let's say that you have an executable called loader.exe. You can create a virtual folder on your company's Web server and let the user know that the executable is available at http://www.mycompany.com/myapp/loader.exe.
Users can run the executable by pointing a browser to the URL (see Figure 1.) Users can also run the executable by entering the URL via the Run option in the Start menu. Or, you can create a Web page with an HTML HREF tag referencing the URL.
Let's run one of the .NET executables that comes with the downloadable files for this article:
- Unzip the CodeDownloadDemo.zip file to c:\CodeDownloadDemo folder.
- Create a virtual folder in IIS and name it CodeDownloadDemo
- Map this virtual folder to your c:\CodeDownloadDemo\Loader\Bin folder
- Launch Internet Explorer and enter the following URL: http://localhost/CodeDownloadDemo/loader.exe
See how the loader application comes up outside the browser? Once the application is running, you can even close the browser and still access the application.
You can do the same with any other .NET executable. Just drop it on a Web server and launch it with a browser.
Watch Out for UrlScan Tool
The example above runs seamlessly as long as you don't have the UrlScan Tool installed on your Web server. UrlScan Tool was distributed as part of the IIS Lockdown tool to stop invalid URL requests made to Web servers. By default, UrlScan Tool considers URLs invalid that include an executable name on them. So running the UrlScan Tool will deny access to http://localhost/CodeDownloadDemo/loader.exe.
You can detect whether you have UrlScan Tool installed in your system by reviewing the list of applications installed on your computer (Figure 2.)
You can also configure UrlScan Tool to allow requests for executables. To do so, modify the INI file that UrlScan Tool uses to detect valid and invalid requests. This file is usually named c:\windows\system32\inetsvr\urlscan\urlscan.ini. Below is a fragment of this file.
[DenyExtensions]
; Deny executables that could
; run on the server
.was_exe
.bat
.cmd
.com
Notice that the .exe file extension has changed to .was_exe to indicate to the UrlScan Tool that .exe extensions are not to be denied. Keep in mind that this is a quick-and-dirty way of working around this restriction. You should read the UrlScan Tool documentation and consult with your network administrator before making this (or any other) change to a production Web server.
You might need to restart IIS after modifying UrlScan's INI file in order for IIS to pick up the change. You can restart IIS by running IISRESET.EXE program from the DOS prompt.
Auto-Deploying the Rest of the Application
So far, you've seen how to deploy an executable over HTTP. This works fine for small applications like the loader (45KB). But applications are typically much larger than 45KB. What if your application is 3MB? You wouldn't want your users to download 3MB from your Web server every time they run the application. This brings us to the second piece of auto-deployment.
A better approach is to install a copy of the main executable on the users' hard disks and let this main executable download the rest of the application on an as-needed basis. For example, users could download a few kilobytes when they run the Invoicing module of your application, another few kilobytes when they run the Employees module, and so forth (see Figure 3).
This type of deployment is commonly known as trickle down deployment. .NET provides the tools to incorporate this trickle down deployment out-of-the-box. The classes that provide this functionality are in the Assembly class in the System.Reflection namespace. The Assembly class can download an assembly from a remote location and cache it for future references.
You could use the following code to download assembly ModuleA.DLL from http://localhost/CodeDownloadDemo/Loader/ and instantiate class EmployeeForm from this assembly:
'Define URL.
Dim URL As String
URL = _
"http://localhost/CodeDownLoadDemo/ModuleA.DLL"
'Load assembly from the URL defined above.
Dim a As [Assembly]
a = [Assembly].LoadFrom(URL)
'Get a reference to EmployeeForm class.
Dim t As Type = a.GetType("ModuleA.EmployeeForm")
'Create an instance of the form and show it!
Dim o As Object = Activator.CreateInstance(t)
o.Show()
This simple code snippet illustrates several interesting features of the .NET Framework. Let's review them.
First of all, the line [Assembly].LoadFrom downloads a DLL via HTTP. In this particular example, the URL happens to be localhost, but it could just as easily be a remote location, like http://www.mycompany.com/myapp. Just like that?one line of code with the right URL and .NET does the rest of the work, regardless of the assembly's location.
Secondly, the code a.GetType retrieves a reference to a class inside this DLL that you have just downloaded from a remote location. Notice that the name of the class is a string, which means that you could define at runtime the name of the class that you want to reference. This is very important because now, at runtime, you can decide whether you want to reference ModuleA.EmployeeForm or ModuleA.InvoiceForm. For strong-typed languages like VB.NET and C#, this is a very important feature in .NET as it provides a degree of flexibility not common to these types of languages.
Finally, the code Activator.CreateInstance(t) creates an actual instance of the EmployeeForm class. Once you have an instance of the class, you can start calling its methods and setting its properties. In the previous code, you called the method Show of the EmployeeForm class.
When localhost Is Different from 127.0.0.1
The techniques described in the previous two sections will work just fine as long as you use http://localhost as the URL. However, as soon as you try to run them using http://127.0.0.1 you will start experiencing some difficulties. For example, trying to run the loader.exe with the URL http://127.0.0.1/CodeDownloadDemo/loader.exe generates an error similar to the one depicted in Figure 4.
The exact screen might vary depending on your configuration, but regardless of how the screen looks, you will receive some sort of security error. In Figure 4 the error was System.Security.Policy.PolicyException.
When you run code from a URL, you are (potentially) running code from a remote location. .NET does not have any idea whether the code that you are trying to run comes from a location that you trust or not.
The Security Policies applied by .NET to remote code are rather complex, but at its most basic level, .NET considers a URL that does not include a dot inside it (like in http://localhost) an Intranet URL. By default, .NET lets you run code coming from an Intranet location. On the other hand, .NET considers a URL with a dot inside it (like in http://127.0.0.1) an Internet URL. By default, .NET will not let you run code coming from the Internet unless you explicitly tell .NET that the indicated URL is a trusted location.
It's All About Security
Security Policies in .NET are a necessary pain. Without them, letting users point their browsers to any URL, download a .NET executable, and automatically run it on their computers is every virus writer's dream come true. With Security Policies in .NET, you can protect users from accidentally running code from unknown sources.
You have probably heard before that .NET is highly integrated with the operating system. Security is one area where this integration is particularly evident.
.NET Security Policies are very powerful and highly configurable. Let's take a look at how these policies are structured and how you can configure them to allow users to download executables from trusted URLs.
.NET Security and Internet Explorer Security Settings
The first place where you can configure security settings to allow users to run executables via HTTP is, not surprisingly, in Internet Explorer. To do so, launch Internet Explorer, go to the Tools menu option, select Internet Options, click on the Security tab, select Trusted sites, and click on the Sites button.
With .NET Security Policies, you can protect users from accidentally running code from unknown sources.
Let's add http://127.0.0.1 as a trusted site (Figure 5). Notice that the Require server verification... checkbox is unchecked in order to be able to add this site to the list.
Now that http://127.0.0.1 is a trusted site, let's try to run the executable again with the URL http://127.0.0.1/CodeDownloadDemo/Loader.exe Now the application will actually run, although .NET lets us know that some security issues are still unresolved (Figure 6).
You can see that the loader application actually ran this time. Notice the small information icon on the upper left corner that indicates that this application is running in a partially trusted context and therefore some features of the application might not work. For example, trying to load data from the Employees Form generates a security error (Figure 7). The form is attempting to read data from SQL Server's Northwind database in localhost and the application does not have security permissions to do it.
Notice also how the URL (127.0.0.1) and the word "trusted" have been added to the application's title bar.
.NET Security and the Framework Configuration Tool
Although Internet Explorer allows you to configure some of the security settings for .NET applications, it does not provide a comprehensive list of things that you can allow or deny to .NET applications.
In order to get access to the full range of security options available to .NET applications, you need to use the .NET Framework Configuration Tool. This tool is part of the .NET Framework (not VS.NET), which means that every user with the .NET Framework installed has the Configuration Tool installed as well. To launch it, go to the Windows Control Panel, select Administrative Tools, then choose Microsoft .NET Framework Configuration.
Using this tool, you can configure .NET Security Policies and decide what locations you want to trust and the degree of privileges that a location will receive.
A complete explanation of .NET Security Policies and the tools to configure them is beyond the scope of this article. But let's take a brief tour of how to use the .NET Framework Configuration Tool to configure Code Groups and Permission Sets (Figure 8).
In Code Groups, you categorize which code is to be trusted and which code is to be denied access. You can probably recognize that the predefined code groups in the Figure 8 correspond to the Zones that you saw in the Internet Explorer Security Setting.
Permission Sets are a means to assemble various permissions under a single name. For example, Figure 9 shows the permissions assigned by default to Permission Sets LocalIntranet and Permission Sets Internet.
You can define custom Code Groups and Permission Sets by right-clicking in the appropriate node. For example, follow these steps to give full trust to code coming from URL http://127.0.0.1 so that the previous example does not raise any security errors:
- Open the Code Groups branch, right-click on the All_Code branch and select New (Figure 10). Enter a name for the new Code Group, perhaps MyCodeGroupFor127, and then click Next.
- Select URL as the condition type for the code group and enter http://127.0.0.1/* as the URL to trust (Figure 11), and then click Next.
- Finally, select Full Trust (Figure 12), and click Next and then Finish.
Now, if you go back to Internet Explorer and launch the loader.exe program with the URL http://127.0.0.1/CodeDownloadDemo/Loader.exe, the application will run without Security problems. You can even go to the File menu, and then launch the Employee Form and load Employee data with no problems.
In the previous example, you gave Full Trust to a URL. This is not a problem for a demo; but in production environments you need to be more selective and give locations the minimal permissions required in order for them to work properly. Your network administrator will probably not let you grant permissions otherwise.
I highly encourage you to play with this configuration tool and learn about how to create groups and permission sets that will match your particular security requirements.
Deploying .NET Security Policies
In the previous section, you created a security policy to trust code coming from the URL http://127.0.0.1. You wouldn't want to have to ask your users to follow the same steps as you did to trust your assembly on their computers, would you?
Fortunately, once you have defined a Security Policy, you can distribute it to your users and have them add it to their system with a single click.
You can use the .NET Framework Configuration Tool to configure a wide range of security options available to .NET applications.
After you define a Security Policy, you can create a Deployment Package that installs the Security Policy via a Windows Installer File (an .MSI file.) To create a Deployment Package, simply right-click on the Runtime Security Policy branch, and select the Create Deployment Package option (see Figure 13).
When you create a Deployment Package, you can select whether you want to use Enterprise Policies, Machine Policies, or User Policies. For this example, let's create a Deployment Package using Machine Policies as shown in Figure 14. Notice that the extension of the Deployment Package is .MSI.
Once you have created a Deployment Package, you can ship this file to your users. Running this file automatically creates the security policies that you defined on your computer on their computers.
When users run this .MSI file (by double-clicking on it in the Windows Explorer) the Windows Installer picks it up and executes it automatically.
In addition to this, a network administrator can push these files to users automatically by configuring them on the network.
Assembly.LoadFrom Revisited
Now, let's review what .NET does behind the scenes when you use the Assembly.LoadFrom method to load an assembly from a remote location. Let's take this code for example:
'Define URL.
Dim URL As String
URL = _
"http://localhost/CodeDownLoadDemo/ModuleA.DLL"
'Load assembly from the URL defined above.
Dim a As [Assembly]
a = [Assembly].LoadFrom(URL)
When .NET executes the LoadFrom method, it goes to the URL http://localhost/CodeDownloadDemo and looks at the ModuleA.DLL assembly on the Web server. .NET verifies whether you have downloaded this assembly before. If you never have, .NET downloads this assembly and stores it in the .NET Assembly Download Cache.
The second time you run this same code, .NET realizes that the assembly has been downloaded before. If the version in the Web server is the same as the one that you have in your Assembly Download Cache, .Net uses the one that you already have on disk.
Let's say that you run this code a third time and .NET realizes that the version on the Web server is more recent than the one that you have on disk. .NET automatically downloads the new version, stores it in the Assembly Download Cache, and runs it.
The Assembly Download Cache is a special folder located under c:\windows\assembly\download. Figure 15 shows how the assembly cache looks on my computer.
A very important consideration to keep in mind is that Assembly.LoadFrom downloads an assembly from the server if its datetime stamp is more recent than the one located on the user's disk. This is very important: .NET compares dates, not version numbers. Keep this in mind if you have multiple developers compiling executables and DLLs from different computers and their time zones are different.
What if the Web Server Is Not There?
What if you execute Assembly.LoadFrom and the Web server at http://localhost/CodeDownLoadDemo/ is not available? For example, what if your user lost her Internet connection or she is on the road with her laptop. In this case, LoadFrom fails and the user won't be able to load your classes from this assembly.
There is a workaround for this. It is not a pretty workaround, but it works. You (or your user) just need to switch Internet Explorer to work offline. Once you have done that, Assembly.LoadFrom automatically knows that it needs to look for the assembly in the Assembly Download Cache.
Requirements
There is a downside to all of techniques described above. The problem is that for automatic deployment to work on the users' computers, they must have the .NET Framework installed on their computer.
Although the .NET Framework is available for free, not everybody has a copy installed. Asking users to download a 20MB file and install it on their computers might not be a favorite characteristic of your application.
In the future, this will be less of an issue as new computers running the Windows operating systems start having the .NET Framework already installed. In the meantime, you can create typical installation packages using tools like Install Shield or Wise Installer and have these tools automatically install the .NET Framework.
Summary
.NET Automatic Deployment is a very powerful technology that you can use to reduce the hassle associated with deploying fat client applications. Now you can provide users with rich user interface applications that always run using the latest version without them having to keep going to a Web site to download updates.
.NET Framework provides the foundation needed to make this automatic deployment happen. .NET downloads code from a remote location when it's appropriate to download it and caches it for future reference.
A very important feature of this technology is security. .NET lets users download code from remote locations while at the same time protecting them from inadvertently executing code developed by somebody they don't know.
This article barely touches the surface of what .NET Automatic Deployment can do. I highly encourage you to start experimenting with this new technology and see how you can take advantage of it.
Download the complete source code for this article at: http://visionds.net/hcorrea/CodeDownLoadDemo.zip