Windows Installer, The .NET Framework, The Bootstrapper, and You
When I was in college I believed in the notion of bug free software. You heard me, bug FREE. If you could write one line of bug free code, you could write two lines of bug free code. And if you could write N lines of bug free code you could write N + 1 lines of bug free code. Proof by induction! But alas, in my youth I had no concept of one of the most detrimental factors in a large software project: time.
Steve McConnell, of Code Compete fame, believed that if you think enough about a problem up front you can foresee and eliminate the majority of issues before they become issues. Design, planning, specifications. That idea is just as true today as it was in 1993, but unfortunately planning can only take you so far. The thing that forethought can never overcome, by definition, is change.
I would like to tell you a story of change. This story is about Windows Installer (code named Darwin), the .NET Framework, and their relation to the Bootstrapper. How although we thought hard about getting things right the first time, our product is constantly in flux and time itself is what brought in these changes.
Long time ago
In order to install an MSI (Windows Installer package) created via Setup Project you need to have Darwin 2.0 installed on the target computer. So we created a Bootstrapper package specifically for installing Darwin 2.0.
The .NET Framework redistribute package (dotnetfx.exe) included Darwin 2.0; more specifically the 3MB redist was inside the 20MB dotnetfx.exe. To communicate that to the user at build time, in case they selected both Windows Installer 2.0 and .NET Framework Bootstrapper packages, we added the following elements to the .NET Framework Bootstrapper package:
So you would get a build warning indicating that you are deploying more prerequisites than are required.
As time went on the .NET Framework setup team wanted to reduce the size of the .NET Framework redist. Since Darwin 2.0 is installed as part of the OS on 2K, XP, and 2K3 (all except for Windows9x), it was removed from the dotnetfx.exe redist. Although the dotnetfx.exe still required Darwin 2.0 be installed on the target machine, it just didn’t bundle the Darwin redist inside.
The Bootstrapper still marked the .NET Framework package as including Windows Installer 2.0, but it had to add the Darwin redists as separate package files. The Bootstrapper wouldn’t download them unless needed, and they would only be needed if you were installing the .NET Framework on Windows 9x.
We accomplished this with the following manifest changes to install Darwin 2.0 as a separate package file. Note that we skip installation on any version of WindowsNT or if it is already installed.
Actually, we added two package files: instmsia.exe and instmsiw.exe. Instmsiw.exe is the Unicode-enabled version of the Darwin 2.0 redist and was used for Windows 2K Gold, which by default only has Darwin 1.1 installed.
Later the .NET Framework dropped support for Windows 2K Gold and so we no longer needed a reference to instmsiw.exe.
A new version of Windows Installer comes out, version 3.0. This new version offers some great, new features, so the .NET Framework took a dependency on it. Meaning that to install the .NET Framework you need Darwin 3.0 installed.
Now the trick here is that Darwin 3.0 won’t install on Windows9x, Windows 2000 Gold, or Windows 2000 SP1. So the .NET Framework actually requires Darwin 3.0 unless you are on 9x. The .NET Framework redist then dropped support for Windows 2000 Gold and SP1.
To update our manifests to accommodate this change we added the Darwin 3.0 redist to the .NET Framework manifests. We add the same VersionNT and VersionMSI install conditions as the Darwin 2.0 package.
Darwin 3.1 is released to address a few bugs. The .NET Framework team discusses taking a dependency upon Darwin 3.1, but eventually decided not to. One reason for this is that upgrading Windows Installer version requires a reboot, which nobody likes. (This wasn’t an issue when taking the Darwin 3.0 dependency because both XP SP2 and 2K3 SP1 include Darwin 3.0 as part of the OS.)
The SQL Server Express redist however relies on some Darwin 3.1 functionality, and their redistributable does need Darwin 3.1. This sounds like a great win, but from the Bootstrapper’s perspective this is a serious problem.
Worst case scenario: User uses the Bootstrapper to install SQL Server Express on Windows 2003 Gold. First the Bootstrapper needs to install the .NET Framework 2.0. This requires Darwin 3.0, so the Bootstrapper installs it and reboots. Then the .NET Framework installs. Then the Bootstrapper installs SQL Sever Express. That requires Darwin 3.1, so it gets installed and the machine reboots again.
Needless to say two reboots for a common deployment scenario isn’t acceptable.
We add a new Bootstrapper package specifically for Darwin 3.1 to address this.
The .NET Framework package will now install Darwin 3.1 unless 3.0 is detected. Meaning that if the machine meets the Darwin version requirements we won’t upgrade it, but if we do need to upgrade we will push out Darwin 3.1.
The SQL Sever Express package then takes a dependency on the new Darwin 3.1 package. Expressed as follows:
This way if the .NET Framework package didn’t install Darwin 3.1, then the Windows Installer 3.1 package will. This way at worst a SQL Server Express Bootstrapper deployment will require one reboot.
Obviously we would have liked to known the Darwin requirements for each Bootstrapper package a year ago, to avoid all these tweaks and updates. But as mentioned earlier, time usually introduces changes from other teams whether you could foresee it or not. Fortunately the Bootstrapper is extensible enough to accommodate these changes without too much extra dev/test costs.
But the moral of the story is that robustness will always trump spec thoroughness in the long run.
[Disclaimer: As you can imagine, this information is subject to change between now and when we ship Visual Studio 2005, codenamed Whidbey.]