Creating an Uninstall-First Upgrade MSI

Here I’ll cover the easy method-an uninstall-first upgrade MSI. The easy method is by far the fastest one to develop, but has some serious disadvantages:

  • The uninstall of the old package might remove any registry keys and INI files that store the user preferences and customizations, which can range from a big annoyance to a training issue (if users have to be taught how to fix their settings again).
  • If the previous package’s uninstallation is not very clean, there are likely to be remnants even before your new package installs. Little oversights like EXEs or services which are not shut down during the uninstall could mean that the uninstall process just leaves the files there, and if they need to be updated Windows Installer cannot do it while they are in use.
  • If the previous package has to do a reboot after the uninstall, the system will not automatically return you to the installation process.

There may, however, be work-arounds for each of these problems, which I’ll discuss below.

In contrast, a real upgrade MSI has less disadvantages, with one major exception: it is very difficult to create if you are not the program developer who created and knows the old and new versions of the package. I’ll cover that process in a separate document (Creating a Typical Upgrade MSI with the WPS UpgradeSync Tool).

Uninstall-First MSI Upgrade

Your upgrade MSI can have custom actions that kick off other MSIs, so this is the perfect way to force the de-installation of the previous version. Here’s the how-to:

  1. Create, edit and test your package for the new version of the program as you normally would (from a SetupCapture, transform, etc.). All your edits from this point on will go in this new package, not in the old one.
  2. WIE can help you configure the other values needed to tell Windows Installer that this package is designed to upgrade the old MSI. Go to the “Installation Expert” pane, and scroll down the list on the left to the last box, labeled “Distribution”. Select “Upgrades” from this list to show the Upgrades page on the right side of the screen. Click the “Add” button, and browse to your previous-version MSI. WIE will then grab the Upgrade Code, Version Numbers, and Language values it needs (and allow to modify them if needed).
  3. On the bottom half of the dialog box is the “Upgrade Action” block; here WIE will help setup the uninstall action for you. In the “Features to remove” box, you probably want to enter “ALL” to uninstall everything (unless you have some reason to do only parts). The “Action Property” drop-down is where you name the action that will do the uninstall-remember this name in case you need to change the scheduling options. Again, in most cases you’ll want to check “Migrate feature states”-generally that’s only for typical upgrade MSIs, not for the uninstall-first method, unless you want it to pick-and-choose what features to install based on what the user had installed before.
  4. If you find the above method doesn’t work well for the uninstallation, you can setup it up manually with the following process (otherwise skip this unnecessarily complicated bit):
    1. In WIE, go to the “MSI Script” pane, and drop the “Installation Mode” box at the top to select “All Custom Actions”.
    2. From the “Actions” box on the left, double-click “Execute Program from Destination”. Name your custom action something like “UninstallPrevious” (that’s what I’ll call it here from now on). Use “C:\Windows\System32” as the working directory (where MSIEXEC.EXE should be found.) Your command line should look something like this:

      msiexec.exe /x {GUID_OF_OLD_MSI} REMOVE="ALL" /qn

    3. On the “Location” tab, uncheck the “No Sequence” box and drop the Sequence menu down to “Normal Execute Immediate/Deferred”. Highlight “InstallInitialize” in the list, then click the “Add” button to add your new action just below “InstallInitialize”. In the “Condition” box at the bottom, type in “NOT Installed” so that this action only runs when you first install your upgrade MSI.
    4. If you want the package to run even if the old version was not found, you’ll have to add another custom action to set a property if the old version if found, then use that property in the condition of your UninstallPrevious action. Follow the directions in the file “Creating A Launch Condition To Check If An Application Is Already Installed” to set the property and add the condition like this: ‘APPDEPEND~><“file.exe” AND NOT Installed’.
    5. On the “Properties” tab, leave the “In-Script Options” on “Immediate Execution”. You may have to play with the “Processing” options a bit-I would normally use “Asynch, Wait at end of sequence” so that Windows Installer would not continue with the rest of the install until the uninstall was done.
  5. If this is a major update (most likely), you’ll probably want to change the Product Code and you definitely want to change the Package Code in your upgrade package.
  6. You know the routine: compile and test, test, test.

Possible Work-Arounds for Common Problems

Work-around to Keep the User Settings

This would be a complete custom-job, but if your previous version MSI removes the user’s custom preferences and settings and such, you can make an effort to save them first. You’ll have to figure out which files, registry keys, or INI files to save, and create custom actions to save them off to a temporary directory before uninstallation the old version, and copy them back at the end of the new installation. Have fun…

Work-arounds for the Files-in-Use Problem

If the uninstall procedure for the old MSI doesn’t work completely because the files are in use, you might be able to add standard & custom actions to stop the processes and services before you the uninstallation is kicked off. Here’s how I would do it:

To stop a program that’s running before the uninstallation, use the Task Manager to figure out which process name(s) you need to kill. Get a copy of the free SysInternals’ pskill utility and create a custom action to run it with the process name as the command line argument. Schedule this custom action before your UninstallPrevious action, and set it to ignore the exit code so that Windows Installer will continue whether or not it found and stopped any processes.

To stop a service that may be running, WIE gives you a GUI interface to the ServiceControl table. Go to the “Installation Expert” pane and the “Services” tab. From the “Add” button on right side of the screen, add a new “Service Control” function. If your upgrade MSI installs the service, you should have the service name as an option in the drop-down list; if not, you’ll have to enter it (use a tool like SC.EXE or REGEDIT.EXE to figure out the short/system name rather than the user-friendly long name. Check the box to stop the service during the install and “wait for service action to complete before continuing”. Note that you may have to play with the sequencing tables to be certain that this action runs before your UninstallPrevious action runs.

Work-arounds for the Reboot Problem

If the uninstallation of the old package demands a reboot to complete the uninstall, there are two possible work-arounds:

  1. The reboot may not be necessary at all, especially if your target machines are only Windows XP. Get to know the rules of when a reboot is really needed and when you can ignore it. To suppress the old package’s reboot during the uninstall, add the property ‘REBOOT=”ReallySuppress”‘ to the options on your UninstallPrevious action’s command line.
  2. If the reboot is really needed, then you may be able to script a way to have the system continue the installation process after the reboot. Maybe someday I’ll make this into a handy-dandy VBS script, but until then what you need to do is add a value to the HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce registry key that launches your upgrade MSI again, maybe with a public property that tells it not to run the UninstallPrevious action again via the condition on that action. It’s a bit of work, but maybe worthwhile to make your upgrade package work smoothly.