Deployment is the enemy of smart-client applications :-)
A big problem in building component-based systems out of plain old DLLs was the versioning and deployment issues that arose (commonly known as “DLL Hell”). Many of the same problems (and some additional ones) applied to building component-based systems out of COM components.
.NET came along and solved a good number of these problems and a few more for good measure. By default, the deployment model for a .NET application is the "private" deployment model where you install your application into its own folder and it doesn't mess with any body else. Within that folder you can use your own versioning system driven through configuration to make sure that you load up the bits that you expect to load up.
If private deployment doesn't work for you (and it typically wouldn't for a component vendor such as a producer of a grid control) then you and your consumers get to be very explicit about things and the component gets placed into the GAC where the framework nicely provides side-by-side versioning.
So...all in all a great story but it leaves the question of how you get the bits onto the box in the first place.
.NET framework V1.0 and V1.1 had a technology that I used to call "HTTP download" but, since, seems to have been renamed "Zero Touch Deployment". What this allowed was you to embed a link to a .NET assembly into a web page and when the user clicks on that link to the assembly the bits (along with dependencies) get downloaded over HTTP to the client where they run in a fairly tightly controlled sandbox.
In V1.0 the sandbox by default was so tight that the assemblies didn't run at all but V1.1 had them running. The bits would be assigned security permissions as they were loaded (based upon the evidence they provided such as where they came from, whether they were cryptographically signed by a trusted party and so on) and, by default, applications that came from the untrusted internet were given very few permissions at all. If you wanted more permissions for your application then, typically, you would have to pre-configure the client's code-access-security policy to allow that which could be a sticking point.
For applications downloaded this way, there's also a secondary "problem" in that the HTTP download technology assumes a network connection so once you've got an HTTP downloaded app you can't work with it in a disconnected scenario even if you already have all the bits on your hard drive. This is a second sticking point.
After .NET Framework V1.1 the people on the MSDN Patterns site put together a bunch of technology named the "Application Updater Block". The link is here. This is a pluggable set of components which you can use to build applications that download and update themselves "automatically" - I use quotes here because it's automatic in the sense that your code gets to be in control of the automatic nature of it.
What this gives you is a framework for doing automatic downloading and updating of your application's bits based upon configuration that is both client-side (how often updates should be done, what applications need updating) and server-side (what's the current version of the app, what bits make up that version) and so-on.
So, using the updater block allows you to deploy a very lightweight "shim" application to the client PC which can be used to manage the deployment of one or more applications to that PC by downloading versions of the applications over HTTP from a web server. A developer of an application builds a "manifest" describing the bits in the application and digitally signs the manifest and the bits and the "shim" watches for new manifest versions being published and then fires events into your code to determine what to do (e.g. prompt the user or force the user to download the new bits). The transport is pluggable (BITS by default), the verification is pluggable and there is also the opportunity to run code "post-install" to handle things such as configuration file schema upgrades.
It's worth pointing out that the bits that get downloaded end up run from the local hard-drive and so, after download, they have full trust with respect to code-access-security on the machine.
So...how does this change in .NET Frameworks V2.0?
As I'm sure you've already heard, there are a new set of technologies named "ClickOnce" which provide similar but extended functionality to what was previously provided by the "Zero Touch Deployment" and "Updater Block" models.
The first thing to understand about ClickOnce is what it can and can't do. There's a really good list here but in essence ClickOnce is about deploying .NET applications that;
- Require the presence of the .NET framework V2.0 on the client
- Are installed to the client from web-server, file-server or CD ROM
- Can be configured to enable running both with/without the presence of a network connection
- Automatically update themselves from a specified update location.
- Install into the ClickOnce application cache (i.e. not Program Files or anywhere else)
- Install for one user at a time
- Install no shared files (and no GAC'd components)
- Don't install registry items, don't add favourites, don't register file extensions, etc.
There are some interesting side-effects of some of these items that I'll try and spell out through the rest of this post.
So, how do you publish an application through ClickOnce?
Visual Studio.NET has automatic capabilities for this but I wanted to play around with it a little outside of Visual Studio.NET as I tend to find that’s the best way for me to understand something rather than just use it.
As a starting point you’re going to need an application. Once you have that (a collection of assemblies, configuration files, resource files, etc.) you need to use tools to generate two views of the application.
The first of these is called an application manifest and describes the bits that make up the application. There’s a new tool in the .NET framework named mage.exe (there’s also a UI variant named mageUI.exe) which assists with making application manifests. Details on these tools are here and here
In a nutshell, you run mageui.exe, use the File menu to make a new application manifest and then you provide the details of what your application looks like in terms of some metadata, the set of files that make up the application and the permission set that the application requires on the client.
When selecting permissions the standard permission sets are present in the tool (e.g. intranet, internet) and you can also use the custom permission set to build up your own particular requirements which (today) seems to require typing in a lot of XML J
When you produce the application manifest you have the option to digitally sign it – we’ll return back to this at a later point but, right now, it looks like a bug stops you going forward if you don’t sign it. By convention the extension for an application manifest file seems to be application.exe.manifest
The same mageui.exe tool can be used to produce the second file that you need for deployment which is called a deployment manifest. Again, you run up mageui.exe and click File->New to make a deployment manifest into which you type some metadata describing the application along with;
- Run location for the deployment (e.g. http://localhost/test/test.deploy)
- Whether the application installs locally or is always run from the distribution point (i.e. does it have a Start menu entry or do you need the URL to run it?)
- Whether the application automatically updates, when it does this (startup or later), how frequently it checks and whether it forces updates on users
- A reference to the application manifest which gets read by the tool right there and then.
Again, by convention the naming for a deployment manifest appears to be .application or .deploy
Having got these 2 files and your application you can test it pretty quickly. Make a virtual directory on your machine that matches what you specified in the tools as the deployment location and copy your bits to it such that you have something like;
C:\inetpub\wwwroot\testApp\testApp.deploy
C:\inetpub\wwwroot\testApp\1.0.0.0\testApp.exe.manifest
C:\inetpub\wwwroot\testApp\1.0.0.0\testApp.exe
Then drop an HTML file into your virtual directory which has a link to the .deploy file, view it in IE and click the link to deploy.
When I deploy my test application in this way I get some pretty strong warning dialogs. The first is that the signatory of the application is not someone who is known to me and the second is that (because of my choices earlier) the application is requiring full trust on the computer.
Going ahead with the installation gives me a new entry on my Start menu for this application and any further launches of the app from the web page simply run the app rather than going through the install process for the second time.
Upgrading the application is now relatively easy. I change the application code and rebuild it. Having done that I use the mageUI.exe to reload my .deploy file and my .exe.manifest file and change their versions (and to reload the .manifest in the .deploy so that it picks up the new version). I then go and copy the new executable and its manifest to a 2.0.0.0 folder on my web server and re-deploy the .deploy file.
When I run the application I get the expected behaviour of launching the new version of my application (note: if you’re playing with this stuff be sure that Internet Explorer is not caching things when you want it not to).
Now I’ve got 2 versions of the program I can go to the Add/Remove Program tool, find my application in there and use it to roll back the installation of V2.0 that I just added to the machine or I can use it to remove the application altogether.
What’s happening in the file system?
So, where are these applications being placed? For a proper explanation see here which explains that the ClickOnce cache is a per-application-version, per-user cache for which a user gets by default 100MB of storage. Into this storage are placed the code files, configuration files, settings and data. Note that for “online always” applications every installed version is remembered whereas for “locally installed” applications only version N and N-1 are remembered and stored here.
From a quick use of Process Explorer (http://www.sysinternals.com) you can see that the application is being run from the equivalent of;
C:\documents and settings\user\local settings\apps\
and under that folder I get a couple of sub-folders for my single-app installation. One is named Data and the other is a mangled name based, presumably, on names or keys in my app. The structure under this folder is something like;
Data\XXX\YYY\testapplication.app_2.0.0.0\Data
Data\XXX\YYY\testapplication.app_3.0.0.0\Data
RRR\WWW\manifests
RRR\WWW\testapplication.app_*
Where XXX, YYY, RRR, WWW represent generated names too ugly to type out! And the testapplication.app_* represents a bunch of folders for each version of my application.
I don’t know of a way right now to list all the applications that you have installed in this set of folders in the same way that you could use the gacutil tool to list what you had in the download cache in existing versions of the framework.
So, what’s the purpose of the Data folder?
It turns out that a ClickOnce application has a bunch of choices when reading/writing data with some added in framework V2.0.
There’s a good starting document here but the new storage area is known as the ClickOnce Data Directory and it looks like that’s what these folders are for.
What’s the purpose of this store? Well, to me it feels very similar to the Isolated Storage space that we had in framework V1.0 and V1.1 for Zero-Touch Deployment applications but this space is partitioned on a per-user, per-application and per-version of that application.
Note that data files are handled differently than, for example. configuration files – the idea of a data file is truly one of an external file that the application needs to work on (so maybe a .MDB file or, interestingly, maybe it could be a MDF and an LDF that you use the new features in SQL Server Express to attach to when the app launches). Note that data files are destroyed if the application is removed.
In order to deploy a data file you have to mark it as a data file when you build your manifest. When you use the mageUI.exe application and add a file to the manifest for the application you get to choose (using the File Type drop-down) whether the file is a data file or not.
In order to get hold of your data file from your managed code you can write code such as;
string dataDir = (string)AppDomain.CurrentDomain.GetData(
"DataDirectory");
Note that you’ll need read and/or write permissions to fully interact with this file. There are friendlier ways to get to this folder if you’re writing a Windows Forms application but it’s a single line of code either way.
Note that for the new attachDbFilename for a SqlConnection connection string on SQLEXPRESS you have a special token named “|DataDirectory|” which seems to map out to this path for a ClickOnce application.
When you install a new version of an application all the data files from the previous version will get copied over into the new version’s data directory. If there are collisions between the existing copied-over-versions of files and new files that the later version is installing then the newer version wins. However, the old files will be placed into an inc folder so you can at least get at them.
So, what about security?
The explanation on security starts here. By default, a ClickOnce application from the web will be assigned permissions based on the “Internet Zone” permissions configured through code-access-security policy on the machine. Installations from a network file-share get treated as the “Intranet Zone” and installations from a CD get full-trust.
Note that when bits are installed by ClickOnce from the “Internet Zone” they do not suddenly inherit full-trust at the point where they hit the hard-drive. Their original location is “remembered” and the same set of permissions is given to them each time they run.
So, by default at the point where I install an application from the web which has not specified any additional security requirements the ClickOnce infrastructure is going to attempt to determine (based on signature) who is the publisher of the software. If this can’t be determined then I get a warning stating that the application publisher is unknown and I can choose to cancel the installation.
If I go ahead with the installation then the application is given the “Internet Zone” set of permissions and if that application then attempts to wander beyond the boundaries of the permission set then it’ll fail.
This would be fine if you never needed to be given permission sets beyond the default set granted to the zone that you’re coming from but that’s unlikely to be enough to get most applications working.
If you need more permissions than the default set granted then there are two ways that can be achieved.
The first (and simplest) is that when you build the application manifest you ask for the additional permissions that are needed.
In this case when the application comes to installation the user is going to be prompted with a dialog that informs them that you’ve asked for more permissions than you should be getting. It becomes one of those “user-choices” at this point as to whether they click “Ok” or not but note that an administrator can control their choice here.
The second mechanism is to build what’s known as a Trusted application. The lowdown on this is here but the basics appear to be pre-configuring a client to allow it to grant higher permission sets to applications that have been designated as trusted in some fashion. The way this works is that the application’s manifest will have a trust-license embedded in it which states that it’s trusted. The deployer has to request a trust-license for the app from the authority who originally pre-configured the client.
Programming ClickOnce deployments?
It looks like you can write code against ClickOnce and take control of the process - I’m planning to revisit this in a later post.
Posted
Mon, Jul 5 2004 8:36 AM
by
mtaulty