ClickOnce - Custom Bootstrapper Packages

ClickOnce Application: Custom Bootstrapper Packages With Separate Installers for 32- and 64-bit Platforms

Disclaimer

Why am I writing an article about this? Simply because I haven’t found one when I needed it. Without pretending to have mastered ClickOnce deployment system or all the ins and outs of packaging for the Bootstrapper, I will simply explain the problem I faced and what I managed to find out and test. If you need a complete reference for either of the systems, I am sure there are good sources for that.

Background

Let’s say a couple of words about the technologies involved. ClickOnce is Microsoft’s deployment vehicle for your .NET applications. It allows developers to distribute their applications over internet, take care of prerequisites and maintain applications by automatically downloading and installing updates based on the version number. A prerequisite is any software package which your application requires to be present on the user’s computer before it can successfully run. For example, for a .NET application, a certain version of .NET framework is a prerequisite. The installer has to incorporate the logic to determine whether a required package is present on the system, and if not, obtain it and install.

This last part - obtaining and installing prerequisites - is carried out by a bootstrapper. There have been several different bootstrapper implementations associated with .NET and Visual Studio projects. The current incarnation first shipped with Visual Studio .NET 2005. It was the first truly generic implementation which did not limit developers to a predefined set of components which could be bootstrapped. Well, these are some big words, you may say, but how to really go about this in practice? If we decide to publish our application with ClickOnce, we go to the Publish section of the project’s properties. There we can open the Prerequisites window to specify what to include and what we see there is a list containing some big calibre libraries and it looks exactly as if we are limited to a predefined list. How do we add something custom to bring it to the clients’ machine and run? Further, how do we deal with platform-targeted installers? For example, a product may have separate installation programs for 32- and 64-bit Windows, how will the installer decide what to run? These are the questions that this article attempts to answer.

Bootstrapper packages

Bootstrapper operates with packages. A package is a piece of software which can be identified by a handler which would typically include name and version, like for example

ProductCode=".NETFramework ,Version=v4.0"

Make no mistake though, Package != Installer. A package might contain several installers each applicable to a specific situation on the system where the bootstrapping setup is operating.

A typical package contains a number of binary files and manifest XML files. It is reasonably well described here. Additionally, a quick Google search for “how to create custom bootstrapper package” brings you inevitably to a tool called Bootstrapper Manifest Generator. It is referenced in the text of the aforementioned How-To at MSDN, but you’d have to actually look for a download link yourself. There is a number of broken links circulating. I downloaded my copy from here. The application looks slightly undernourished and has a number of strange quirks some of which are discussed below. But I still prefer it over having to type the XML myself and having to remember hundreds of intricate details.

The tool creates the entire Bootstrapper package structure (not only the manifest). You can add one or several install programs and specify among other things a set of conditions for their execution. This already starts to sound like something we might want to use.

Suppose, our application needs Ghostscript library to be installed to be able to render PDFs. Ghostscript manufacturers maintain two separate install programs, for 32- and 64-bit Windows. Let’s start by obtaining both installers and saving them somewhere. Then let’s create a new project in the Manifest Generator, of type Package Manifest. To begin with, the structure tree on the left only contains the Package level. On the right-hand side at least project name and product code need to be filled in (default code is automatically derived from the name). Next, let’s start adding install files - there is a toolbutton and a context menu for that. Some essential properties which are set for each install files are these:

  • Display name - I didn’t see it do anything. The values are added to the manifest but it seems that they aren’t used, the installer always uses the product name from the upper (package) level. It is possible that these properties were used in earlier versions of the bootstrapper.
  • Arguments - command-line switches which will be fed to the installer. Quite useful for modifying installers’ behaviour, for example to tell it to run silently.
  • Installation Time, Installed Size, Installation Size - allow the Bootsrtapper to show the user correctly paced gauges and calculate information regarding space requirements. These obviously cannot be exact and only have “cosmetic” purpose anyway. I did not have a chance to check the culture settings for installers within a package this time, so we’ll have to wait until another time and another article for that.

We need to configure when install files will be launched. Let’s have a closer look at the Install Conditions tab. The set of conditions applies only to the current install file. The list contains the conditions which may cause the installation of this particular file to be either simply bypassed or failed. According to the documentation, the conditions are evaluated until there is an encountered one which evaluates to True after which the install is either skipped or failed depending on this condition’s setting. If there are no conditions producing True, the default action is Install. In other words, you can put in several conditions causing an install to be skipped or failed and if none of them evaluate to True, the install file will be launched.

Looking back at our example, we must tell the 32-bit installer to get skipped on 64-bit systems and vice-versa. The Manifest Generator provides a list of variables to include in conditions. In some sources I saw a suggestion to use the values VersionNT and VersionNT64 and check whether the values exist or do not exist. This approach did not work for me on my 64-bit Windows 8 and other systems I tested on (I used clean Windows 7 and 8, both 32- and 64-bit on Virtual Box to test my installers). For example, on a 64-bit machine, the value for VersionNT would be set, but not for VersionNT64. VersionNT64 is not mentioned in Bootstrapper’s documentation either so it only makes good sense. I can only assume that it was included in the Manifest Generator’s list by mistake.

How do I know which variables are actually set and which ones are not during installation? Excellent question! When you run the ClickOnce install application, it will make a homebase in your local Temp folder (which nowadays usually is something like C:\Users\<User>\AppData\Local\Temp), in a folder called something like for example VSD2EE.tmp, i.e. <something_random>.tmp. Check the creation date to see what has just been created to find the one you need. In this folder there would be a file called install.log which lists everything that takes place during this ClickOnce setup run. At the very head of the file you find something like:

The following properties have been set:

Property: [AdminUser] = true {boolean}
Property: [InstallMode] = SameSite {string}
Property: [NTProductType] = 1 {int}
Property: [ProcessorArchitecture] = AMD64 {string}
Property: [VersionNT] = 6.3.0 {version}

The remainder of the file is also very useful for troubleshooting, as it says in plain English what has been checked and what the results were.

Use the ProcessorArchitecture property to assess the “bitness” of your target OS. In my first Bootsrtapper package that actually did what I wanted it to, I ended up with following conditions on the install files:

  • For the 64-bit installation file: BypassIf ProcessorArchitecture = Intel
  • For the 32-bit file: BypassIf ProcessorArchitecture = IA64, BypassIf ProcessorArchitecture = AMD64

Now, all that is left for us to do is build the package. The program has a setting (found in menu under Tools\Edit Machine Paths) which is supposed to allow you to set the output folder. However, when I tried to set the path to my bootstrapper’s packages folder (which on my machine is C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\Bootstrapper\Packages), the application raised an exception while, thankfully, allowing to ignore it and continue. So, when I hit the Build button, the Build Results window informs me that the package is created in my default Documents folder. You may want to examine the <Document Folder>\<Package Name>\product.xml manifest file, to check what goes where and how the condition lists end up looking. Then move the entire <Document Folder>\<Package Name> folder to the bootstrapper’s packages location - and you are done!

Next time you open the Prerequisites window from the VS’s Publishing page, the list of available prerequisites will contain your new package, which is even programmed to run a proper install file based on what kind of operating system the ClickOnce setup is running on. Just what we wanted!

Yuri Makassiouk

Yuri Makassiouk

WEBPAGE: https://no.linkedin.com/in/makassiouk

His brain-care specialist, Gag Halfrunt, also said, 'Vell, Yuri's just zis guy, you know?'

View Comments