I've learned some interesting facts about MSI-based setups in the past few days. Working together, they can cause some really tricky and nasty issues if you are unaware of them. (In other words, this is why I'm still at work at 2:00 in the morning.)

This is more proof that MSI setup is not something you can pick up overnight -- you really need to be aware of a lot of "gotchas".

  1. If a feature's install level is 0 during InstallExecuteSequence, it cannot be installed or uninstalled. Even if the rest of the product is uninstalled, if a feature's level is 0 during the uninstall, the feature will be left behind.
  2. AppSearch is never run more than once in an install. If AppSearch runs during the UI sequence, it will be skipped in the Execute sequence.
  3. In a non-elevated setup (a setup launched by a non-admin user but permitted by group policy or approved via UAC), if the setup is run in maintenance mode, properties from the UI sequence are ignored by the Execute sequence unless they are "Secure" properties. Properties that would normally propagate from the UI sequence to the Execute sequence are simply left at their default values (usually NULL).

Fact #1 is scary all by itself. If the feature's level is 0, it will not be touched by the installer. This may or may not be what you want.

Setting the level to 0 is a commonly used method to hide a feature under certain conditions. The logic is: IF (condition) THEN set Level=0. However, you must be absolutely certain that the condition is FALSE if the product is being uninstalled. If the condition evaluates to TRUE, and the feature has been installed somehow, the feature will be left behind (orphaned) when the rest of the product is uninstalled. There will be no way to uninstall the feature. The feature's components are forever stuck on the user's computer. You've just screwed up your customer's machine. Bad programmer, no donut.

As a quick example, consider the following condition (if true, the feature's level will be set to 0):

NOT DOTNET20INSTALLED 

The intent is probably to hide the feature if .NET 2.0 is not installed (.NET 2.0 is a prerequisite for the feature). However, if the user uninstalls .NET 2.0 before uninstalling the product, the condition becomes true during uninstall, the feature's level is set to 0, and the feature is orphaned. Bang, you're dead.

Now let's assume that your users are smart enough to not uninstall .NET 2.0 before uninstalling your product. Being the devious users that they are, they'll probably find some other way to break your install. This is where facts #2 and #3 come in.

Assume your user is using Vista with UAC enabled. The user installs your product as usual. Later on, the user wants to uninstall. He goes to "Programs and Features" (formerly Add/Remove Programs), right-clicks on your product, and selects "Change", then from the installer's UI selects "Remove". Here's what happens:

  1. The install started without Administrator rights, so the UI sequence runs as the normal user.
  2. The DOTNET20INSTALLED property is initialized via AppSearch during the UI sequence.
  3. At the transition to the Execute sequence, the user receives a UAC prompt and approves the install. This enables the Execute sequence to run as a privileged user.
  4. The Execute sequence notes that the UI sequence ran as a limited user, and therefore disallows any non-Secure properties. The DOTNET20INSTALLED property is disallowed, so it remains at the default value of NULL in the Execute sequence.
  5. AppSearch already ran in the UI sequence, so it doesn't run a second time in the Execute sequence.
  6. The condition "NOT DOTNET20INSTALLED" evaluates to true, so your feature's level is set to 0. Bang, you're dead. No donut.

Solutions to these issues will be considered in my next blog. Stay tuned!