Why Windows Installer removes files during a major upgrade if they go backwards in version numbers

Recently, I investigated an issue where some files were missing from a user’s PC after installing version 1 of a product and then upgrading to version 2 of the product.  I wanted to write about how we diagnosed and resolved the issue in more detail in case others run into similar issues in the future.

I started the investigation by looking in the verbose MSI log file for the version 2 upgrade process, and I found entries for the RemoveExistingProducts action that showed the missing files being uninstalled from the PC.  Later in the log, I found entries for the CopyFiles action that showed most of the files in version 2 being installed onto the PC, but I didn’t see any CopyFiles entries for a few of the files that should’ve been installed.

I dug a bit deeper, and early on in the log file, I found an entry like the following for each of the components that contained files that were missing after the upgrade finished:

MSI (s) (60:00) [12:34:56:789]: Disallowing installation of component: {GUID} since the same component with higher versioned keyfile exists

It turned out that each of the files that were missing after the version 2 upgrade had file versions that were lower in version 2 than they were in version 1.  The MSI in question has the RemoveExistingProducts action scheduled after the InstallInitialize action (based on this Windows Installer documentation), and Windows Installer evaluates whether or not it needs to install each of the components in version 2 of the MSI before it uninstalls the components in version 1.  Because a higher version of the component’s key path file is found on the PC, it decides that it needs to skip attempting to install the component.  However, after that decision has been made to skip attempting to install the component, the component is then removed during the RemoveExistingProducts action.

There are a few options to work around this issue, and we ended up choosing option 1:

  1. Re-build the affected files to ensure that they have higher file versions than any previously shipped versions, then re-package version 2 of the MSI with the updated files.
  2. Update the build process for the MSI to ensure that the files in newer builds of the MSI have higher file versions than in any older build of the MSI.
  3. Re-name the affected files and put them into new components in version 2 of the MSI.
  4. Schedule the RemoveExistingProducts action before the costing actions in version 2 of the MSI (as described on Stack Overflow). 

In my scenario, option 2 wasn’t technically feasible because the files in question were built by one team and delivered to another team to include in an MSI, option 3 wasn’t technically feasible because other components depended on the affected files having a standard, well-known name, and option 4 was discarded because it contradicted Windows Installer documentation and led to ICE validation errors for the resultant MSI.