Saturday, November 05, 2005

Upgrading Applications with Microsoft Installer (MSI)

Once we had mastered building a Microsoft Windows Installer (MSI) file (using WiX) and got our application successfully installed, it was time to consider upgrades. We quickly found out that our MSI file would not install if the application already had been installed. It did not matter whether the new install had a higher product version or product id (in which case we want an automatic upgrade) or a lower version (automatic upgrade not desirable).

It turns out that a MSI file in itself cannot bootstrap an automatic upgrade process. When you execute a MSI file (myapp-v0.msi for example) directly the following command get executed:
msiexec.exe /i "myapp-v0.msi"
This will install nicely on a clean machine. To allow for upgrades the myapp-v0.msi must specify an upgrade code.

Now let's see what happens when we release a next version of the application and try to install it on the same machine using a myapp-v1.msi installation file. Doubleclicking on the msi file in the Windows shell will result in a
msiexec.exe /i "myapp-v1.msi"
command. Because there exists an application on the machine that was installed with an installer that had the same upgrade code as we are using now, the default behaviour of Microsoft Installer is to not use the myapp-v1.msi file we started in the first place! Instead it uses a cached version of the MSI file with the same upgrade code. The cached version is a copy of myapp-v0.msi that tends to be saved with a pretty random name in the %windir%\installer directory. So the installation file for a newer product version can never bootstrap the upgrade of a previous version.

Automatically bootstrapping a complete product upgrade requires execution of something like the following command line:
msiexec /i myapp-v1.msi REINSTALLMODE=vomus REINSTALL=ALL
This command will use the myapp-v1.msi rather than the cached version of myapp-v0.msi to perform a (in MSI-speak small or minor) update on the installed product and it will replace the cached copy of myapp-v0.msi with a copy of myapp-v1.msi.

It does not make sense to send end users through command line hell in order to reach installation nirvana, so what else can be done to automatically upgrade older product versions to new versions? The common answer is to use a wrapper executable that invokes Windows Installer (msiexec.exe) with the correct parameters. Commercial installation products such as Installshield tend to provide this functionality. However, open source installer solutions such as WiX (or the NAnt MSI task) don't.

This is where the Microsoft Windows Installer SDK comes to the rescue. The installer SDK can be downloaded as part of the Windows platform SDK and contains the C++ sample code for two utilities that allow you to build your own MSI wrapper executables:

  • setup.exe is the a wrapper executable whose installation behaviour is encoded in a number of string resources.

  • msistuff.exe is a command-line utility to modify the string resources in setup.exe.

Using a command such as
msistuff setup.exe /d myapp-v1.msi /n "My Great App 1.0" /o INSTALLUPD /v 200 /w InstMsiW.exe'
we get a setup.exe wrapper executable that will both install our application on a clean machine or upgrade a previous version when available. It is pretty easy to add this command to a build script, in our case using a NAnt exec task.