Those of you who write application installers using Windows Installer may have noticed a bit of a change in the behavior of version checking in Windows 7 – if you happen to be doing your version checking from a custom action. Let’s have a look.
If you’d like to follow along, here’s what you need to do. Open up Compatibility Administrator. Expand the Applications tree (this could take a minute), and then start typing Windows Installer to quickly scroll down to this entry.
First, you’ll notice a few things which we expect. We’re shimming up msiexec.exe (meaning we’re shimming up every MSI package in the world) with WRPDllRegister, WRPMitigation, and WRPRegDeleteKey. These are all of the Windows Resource Protection shims – so if you have an installer that tries to overwrite kernel32 with some really ancient version, for example, it’ll get fixed up instead of failing.
There’s NonElevatedIDriver – that’s an InstallShield fix, for MSIs created by InstallShield. So far, so good.
Oh.
And there’s VistaRTMVersionLie.
Whazza – huh? (Double takes are completely appropriate here.)
That’s right, we’re shimming up the Windows Installer application to give it a Windows Vista version lie. What does this affect? Any DLL custom action you implement that does a check to GetVersion(Ex) is now going to think it’s getting Windows Vista instead of Windows 7. Do not pass go, do not collect $200, you’ll think you’re on Windows Vista.
However, any application that uses the MSI tables and takes a look at the VersionNT property will be told the correct version of Windows. So, on Windows 7, VersionNT == 601. Just like you’d expect.
From a pragmatic perspective, this improves the overall compatibility of installers on Windows 7 to a huge degree. We did this to systematically fix apps that refuse to install on anything greater than Windows Vista. And it succeeds at this spectacularly.
From a "bonus side effect” perspective (if you like to think that way) – it does serve the effect of pushing people towards using the declarative method of checking versions in the MSI tables, which is our recommended way of checking for specific versions.
Not only is it recommended, it’s the only sensible API we have for checking a version. The others are really hard to get right. How would you check to see if a version is greater than or equal to a specific version? Well, GetVersion returns something really strange until you yank it apart (Windows 98, for example, is 0xC0000A04). How obvious is this?
dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
OK, so that’s a little silly, and GetVersionEx has a structure that simplifies this. But even with the OSVERSIONINFO structures, you can do things wrong. For example, say you want to check and see if somebody is using at least Windows XP. You could easily do that as:
if (osvi.dwMajorVersion == 5 && osvi.dwMinorVerison == 1) {…
And of course that will do the trick while Windows XP is the only thing around that it works on, but as soon as we release a new version of Windows, it breaks. But let’s say you’re trying to get it right, and you ordered one of those fancy computers whose keyboard actually has a “greater than” key – you could still lose the logic. I’ve seen this many times:
if (osvi.dwMajorVersion >= 5 && osvi.dwMinorVersion >= 1) {…
That’ll work on Windows XP, break on Windows Vista, and work on Windows 7. It’s the wrong logic. Here’s what you’d have to do:
bIsWindowsXPorLater = ( (osvi.dwMajorVersion > 5) || ( (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion >= 1) ));
And now we’re complex again.
BUT – with the MSI tables, you can actually use the > key with ease. VersionNT >= 501 will work for Windows XP or greater – for anything we release into the future.
It’s declarative, so you can see the check. It’s easy to get it right. It’s got a lot going for it. Nevertheless, I’ve still seen a lot of people doing things like:
VersionNT = 500 OR VersionNT=501 OR VersionNT=502 OR VersionNT=600
I wonder what they’re going to do for Windows 7?
Now, of course, from the ivory tower we’re always saying “don’t check versions” and we don’t hear the cries that you have to. In the end, it’s a business decision, and I don’t begrudge anybody for saying they can’t afford to test and support something indefinitely on every permutation of system. That’s hard to do. I know you’re going to check for versions. But, there’s an arms race between app compat and the people who want to do the checks. There has even been a suggestion that we *never again* change the version of Windows. I think that’d be downright silly. There are valid business reasons to know what you’re running on.
For example, I’ve got a conversation with a customer going on right now where they work on Windows XP, fail on Windows Vista RTM, and work on Windows Vista SP1. Stuff like that happens. (And, since they were checking from a custom action, they were refusing to install on Windows 7, because they thought they were going onto Windows Vista RTM, where they were known to fail. Ouch.) Ideally, this would use the MSI tables to exclude known bad versions, rather than the unknown.
What I’d like to see is a way to declare supported versions in addition to known bad versions. A known bad version should be blocked – let’s be sensible here. But an unknown version, well if you let it install smoothly, then depending on your relationship with the customer would that indicate a suggestion of support? If it could give some sort of warning “note: this operating system is not supported” but still run, then app compat doesn’t need to come along and remove the launch condition or custom action or verlie the entire framework just to keep apps running.
I get what you’re going for, but I also get that people want their apps to work unless there’s a technical reason why they don’t work. Even if they’re unsupported. Because, for some people, that’s OK. Particularly consumers, many of whom don’t have a lot of money right now to be buying new stuff. Just dazzle them with amazing new features, and they’ll buy it, but people don’t like buying stuff just because you said “don’t work on the fancy new OS that your new laptop came with.”
And so it goes.