If you right click on an executable in Windows Vista, you’ll find a Compatibility Tab, where you can set compatibility modes (layers) for that executable file. These compatibility layers are collections of shims and loader flags (depending on whether they affect the loader or if they have to intercept Windows API calls throughout the lifetime of the process). However, you’ll find that you can’t do the same thing for MSI files on Windows Vista.

With Windows 7, however, you’ll find that you *are* able to apply a compatibility mode. Is this using the same mechanisms? How does it work?

To illustrate, I’m going to take the installer for Bao Nguyen’s excellent Switcher application, hack it up to break it, and then use a compatibility mode to fix it.

Before we begin, make sure you have Orca installed from the Platform SDK…

So, starting with the original installer, right click and select Edit with Orca. From the list of tables, select LaunchCondition. There are 2: one looking for the .NET Framework 2.0, and the other looking for Windows Vista or higher. (Technically, the .NET Framework check is redundant today, but in the future, that may not be true.) Take the VersionNT check, which is correctly written as a >= check, and transform it into a bad check by removing the > character. Now, it won’t install on Windows 7. (Wow – it’s strange to be breaking something instead of fixing something.) Save the modified MSI.

Before we fix it, let’s turn on logging so we can get a better view into what’s happening. Launch the group policy editor (Windows-r – gpedit.msc) and navigate to Local Computer Policy \ Computer Configuration \ Administrative Templates \ Windows Components \ Windows Installer. In the right hand pane, double click the Logging policy. Then, change the setting to Enabled, and modify the policy accordingly. I set mine to iwearucmpvox – gets it all (why not?). Click on OK, and now you should get more data from Windows Installer.

Finally, let’s launch the application. It’s going to immediately gripe to you about the version number. If we look at the MSI logs that are generated (you’ll find them at %temp% – I sort by time to see the most recently generated MSI*.log file), you’ll find the following in the logs:

MSI (c) (8C:1C) [13:03:32:807]: Doing action: LaunchConditions
Action 13:03:32: LaunchConditions. Evaluating launch conditions
Action start 13:03:32: LaunchConditions.
This application requires Windows Vista.

Which we kind of expected, since we intentionally hacked up the MSI to fail like that. (Hey – at least we’re as good at breaking things as we are at fixing them, no? :-) )

Now let’s fix it up. Right click on the MSI, select properties, and then go to the Compatibility tab (which, in our first interesting observation, is actually present). You’ll note that only one option is enabled: running in compatibility mode for the previous version of Windows (the drop down doesn’t offer any more). And, being Windows 7, the previous version is Windows Vista. Right? Let’s find out. Select the checkbox (the dropdown contains only 1 option and is, therefore, superfluous). Now run the installer again, and – surprise! – it works again. Let’s check out what we find in the logs:

MSI (c) (E8:CC) [13:05:37:999]: PROPERTY CHANGE: Modifying VersionNT property. Its current value is '601'. Its new value: '600'.
MSI (c) (E8:CC) [13:05:37:999]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '0'. Its new value: '14'.
MSI (c) (E8:CC) [13:05:37:999]: APPCOMPAT: [DetectVersionLaunchCondition] Launch condition version successfully detected.
MSI (c) (E8:CC) [13:05:37:999]: PROPERTY CHANGE: Modifying VersionNT property. Its current value is '600'. Its new value: '601'.
MSI (c) (E8:CC) [13:05:37:999]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '14'. Its new value: '0'.
MSI (c) (E8:CC) [13:05:37:999]: PROPERTY CHANGE: Adding SHIMFLAGS property. Its value is '512'.
MSI (c) (E8:CC) [13:05:37:999]: PROPERTY CHANGE: Adding SHIMVERSIONNT property. Its value is '600'.
MSI (c) (E8:CC) [13:05:37:999]: PROPERTY CHANGE: Modifying VersionNT property. Its current value is '601'. Its new value: '600'.
MSI (c) (E8:CC) [13:05:37:999]: PROPERTY CHANGE: Adding SHIMSERVICEPACKLEVEL property. Its value is '14'.
MSI (c) (E8:CC) [13:05:37:999]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '0'. Its new value: '14'.

Cool – so we have baked in APPCOMPAT to go through and change the version number to get to the previous version of Windows. Good, right? Well, good if your application is checking for Windows Vista. But the challenge that kept many people from deploying Windows Vista is compatibility with Windows XP – what happens when installers target Windows XP?

Well, it kind of depends on whether Previous version of Windows means the previous version of Windows (Windows Vista) or a previous version of Windows (Windows XP, Windows 2000, etc.). How do we find out?

Hack up that launch condition again!

Go back into Orca, and change the VersionNT check to be = 501 (Windows XP). Save it, and try launching it again. (You’ll have to uninstall the old one first and then install again.) What happens?

Yes – it works! That’s right, we’re kind of like that person you meet in a bar, ask their name, and they respond, “what do you want it to be?” We’ll try different versions until we get one that makes the launch condition succeed. You can see it in the logs:

MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying VersionNT property. Its current value is '601'. Its new value: '600'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '0'. Its new value: '14'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '14'. Its new value: '13'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '13'. Its new value: '12'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '12'. Its new value: '11'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '11'. Its new value: '10'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '10'. Its new value: '9'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '9'. Its new value: '8'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '8'. Its new value: '7'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '7'. Its new value: '6'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '6'. Its new value: '5'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '5'. Its new value: '4'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '4'. Its new value: '3'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '3'. Its new value: '2'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '2'. Its new value: '1'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '1'. Its new value: '0'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying VersionNT property. Its current value is '600'. Its new value: '502'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '0'. Its new value: '14'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '14'. Its new value: '13'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '13'. Its new value: '12'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '12'. Its new value: '11'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '11'. Its new value: '10'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '10'. Its new value: '9'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '9'. Its new value: '8'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '8'. Its new value: '7'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '7'. Its new value: '6'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '6'. Its new value: '5'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '5'. Its new value: '4'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '4'. Its new value: '3'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '3'. Its new value: '2'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '2'. Its new value: '1'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '1'. Its new value: '0'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying VersionNT property. Its current value is '502'. Its new value: '501'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '0'. Its new value: '14'.
MSI (c) (0C:F0) [13:50:10:634]: APPCOMPAT: [DetectVersionLaunchCondition] Launch condition version successfully detected.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying VersionNT property. Its current value is '501'. Its new value: '601'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '14'. Its new value: '0'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Adding SHIMFLAGS property. Its value is '512'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Adding SHIMVERSIONNT property. Its value is '501'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying VersionNT property. Its current value is '601'. Its new value: '501'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Adding SHIMSERVICEPACKLEVEL property. Its value is '14'.
MSI (c) (0C:F0) [13:50:10:634]: PROPERTY CHANGE: Modifying ServicePackLevel property. Its current value is '0'. Its new value: '14'.

Sweetness. Not only can you pass version checks, we even do all of the work for you.

You’ll also note that this is not a shim – it’s a modification of the properties (VersionNT and ServicePackLevel). This is baked right in to Windows Installer. You can still have shims affecting custom actions hosted by the msiexec process (and do – see http://blogs.msdn.com/cjacks/archive/2009/05/06/why-custom-actions-get-a-windows-vista-version-lie-on-windows-7.aspx).

Now, this is more of a consumer-level fix, because consumers don’t use Orca and don’t use transforms. In the Enterprise, you can create MSI Transforms and fix it up without having to cycle through everything. But it’s still nice to be able to quickly fix something when you suspect it’s the problem. Happy fixing!