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.

3 Comments:

Anonymous Anonymous said...

Ahhh, just the info I was looking for. Thanks, this will come in handy.

Fri Dec 09, 03:26:00 pm GMT  
Anonymous Anonymous said...

Hi Gerke. Did you ever try to compile the source code for the setup.exe that ist shipped with the Platform SDK? I got tons of convertion errors but i don't know why. I hope you can help me. matt

Sun Aug 19, 10:23:00 pm BST  
Blogger Gerke Geurts said...

Matt,

I compiled with the Visual Studio Studio 2003 C++ compiler, but have not tried to do so under a more recent version. Later versions of the C++ compiler have added secure versions of many string manipulation functions and deprecated the insecure versions. The resulting warnings/errors can be avoided by added the appropriate defines to the source code.

Regards,
Gerke.

Mon Aug 20, 09:07:00 am BST  

Post a Comment

<< Home