Why Windows 8.1 Sometimes Tells You It Is Windows 8.0


A few years back, I ran some analysis on the shims that were applied to applications (both for commercial applications in sysmain.sdb and for those applications shimmed in our enterprise engagements), and though VirtualRegistry eeked out a victory, if you were to consolidate the version lie shims, they became the clear victor.

This, to me, remains an incredible statement on where we have landed in the world of compatibility. Applications aren’t breaking because of some fundamental change in Windows itself, they’re breaking because the applications themselves don’t believe future versions of Windows will be compatible. In fact, they’re so sure that they are right in their prediction of the future that they flat out refuse to run without even trying.

Now, for shim ninjas, this is the easiest fix you can possibly implement. I can probably whip up a verlie shim with both hands tied behind my back, pecking at they keyboard with my nose. But for normal humans (note: shim ninjas, by definition, are not normal humans), this is very non-trivial. So, what do we do from an OS perspective?

Starting with Windows 7, you probably noticed that we stopped changing the major version of Windows. If you are checking the major version, it remained at 6. It also caught flawed logic in version lies, such as:

if (verMajor >= 5 && verMinor >= 1) {
    // oh, hello, you must be Windows XP or later
   
// (clearly I didn’t test this code on Windows 6.0)
}

So, Windows 7 was 6.1, and Windows 8 was 6.2. For Windows 8.1, we looked for a way to implement something even more compatible, as there were still plenty of checks for equality we wanted to get past. It had been suggested many times in the past that we should just keep the version number the same. Of course, the problem with that is, some people are so desperate to know precisely which version of Windows they are running on that, if we don’t tell them the approved way, they’ll find some other way to find out – and that other way might be harder to version lie to.

But, with Windows 8.1, we had an idea:

We would continue to call ourselves Windows 8.0 … UNTIL you put in a compatibility manifest that says “hey, I’m hip and current and know all about Windows 8.1, so go ahead and tell me the truth.”

Until you put in this manifest, we’ll tell you that you’re running on Windows 8.0 (6.2). Once you do, we’ll stop lying and call ourselves Windows 8.1 (6.3).

Here’s a simple program to test it out:

#include <stdio.h>
#include <Windows.h>

#pragma warning(disable : 4996)

void main(void) {
    OSVERSIONINFO osvi;
    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&osvi);
    printf(“\nYou are using Windows %d.%d\n”, osvi.dwMajorVersion, osvi.dwMinorVersion);
    printf(“\nPress any key to continue…”);
    getchar();
    return;
}

Compile and run this without putting in a compatibility manifest, and you’ll be told that you’re running 8.0. Then, add the following to the manifest:

<ms_compatibility:compatibility xmlns:ms_compatibility=”urn:schemas-microsoft-com:compatibility.v1″ xmlns=”urn:schemas-microsoft-com:compatibility.v1″>
    < ms_compatibility:application xmlns:ms_compatibility=”urn:schemas-microsoft-com:compatibility.v1″>
        <!– Windows 8.1 –>
        < ms_compatibility:supportedOS xmlns:ms_compatibility=”urn:schemas-microsoft-com:compatibility.v1″ Id=”{1f676c76-80e1-4239-95bb-83d0f6d0da78}”></ms_compatibility:supportedOS>
    </ms_compatibility:application>
< /ms_compatibility:compatibility>

Once you include this in the manifest, we’ll fess up and tell you that you’re running on Windows 8.1. (Note: In an effort to optimize performance, explorer.exe does caching. A LOT of caching. So, you may need to do some file renames if you want to play around with changing manifests in real-time, or else risk hitting a cached manifest.)

So, it has, indeed, come to this – we’re lying by default now. But the overwhelming compatibility impact of apps refusing to run for no valid technical reason were the motivation. We’ve marked the version check APIs as deprecated (hence the pragma to disable warning 4996 in my code), and replaced them with ones that make it simpler to do the right thing.

The numbers absolutely back this up. The typical organization has received no benefit from version lies (note: that doesn’t mean that nobody has – there are definitely valid reasons for this, many of them regulatory, just that on average you benefit from fewer than 1 of these). However, the aggregate cost of finding and repairing flawed or unnecessary version checks averages well over a million dollars per organization per migration.

So, if you’re wondering why the version check APIs seem to be a bit flaky, hopefully this explains why. And I’ll wonder right back at you – why do you think you need to do a version check? And, are you really, really sure you do?

Note for Nerds: consolidating the version lie shims isn’t really much of a stretch. Each of the individual version lies actually just passes the version numbers into a shared class that performs the version lie magic.

Comments (7)

  1. Bradley Grainger says:

    I know that you must have stats that showed that "breaking" GetVersionEx was a good idea, but (as a developer who knows how to use that API properly) I've found it incredibly frustrating.

    We primarily use GetVersionEx for logging the OS version for crash reports or submitting user feedback from our app. As it turned out, Windows 8.1 Preview introduced an incompatibility (strictly speaking, it was in the .NET Framework shipped with Windows 8.1 Preview) that crashed our app. If our crash reports had correctly identified the OS as Windows 8.1 Preview, we could have started debugging that immediately. But as it was, they identified it as Windows 8. It wasn't until users posted in our forums about the problem that we received the critical piece of information that they were running on a prerelease OS.

    (And WER is not a fix for this problem; its newest data is often a week or more old. We like having real-time crash reports.)

    Another issue comes when you're writing a plugin for another application. In this case, you can't control the manifest, and you're at the mercy of that app author for whether you can trust GetVersionEx. So now you've put plugin authors in the position where they can't use the approved way, so "they'll find some other way to find out – and that other way might be harder to version lie to."

  2. cjacks says:

    Hi Bradley,

    Yes, unfortunately, you are using the API for good rather than for evil, and the bad actors "spoiled the fun" for everyone, if you will. But, if it helps, we do know the scenario, and we are considering how to implement the best solution so you have a supportable solution to your intent here. I know the Mr. Spock response ("the needs of the many outweigh the needs of the few") isn't satisfying, particularly when the person who gets punished is the person who was actually doing it right, but that was unfortunately one of the hard decisions we had to make and don't make lightly. But hence the blog entry, so at least people can figure out what the heck is going on and why.

    //chris

  3. jader3rd says:

    As long as IsWindowsVersionOrGreater doesn't lie, I'm fine with this solution. Can we get a .Net library version of IsWindowsVersionOrGreater?

    Now that Windows will be lying about the build numbers to apps which aren't in the know, are you free to update the major version numbers? It just feels wrong to have a major release of a software product not update the major version number.

  4. Curt Dixon says:

    I think you have the GUIDs for WIn 8.1 & Win 8.0 mixed up.  According to MSDN, this is the line for 8.1:

         <!– Windows 8.1 –>

         <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>

  5. cjacks says:

    Hi Curt – Sorry for the copy/paste error – I verified in the sources and updated the post! Thanks for the catch!

  6. Steve Dower - MSFT says:

    Is there a shim available to prevent lying about the version (and other shims related to not having the GUID)? I'm thinking of script interpreters (we're especially concerned about Python, which targets XP but does not prevent users from calling later APIs if they want) and programs like Photoshop that have huge plugin ecosystems. Requiring an underlying product update for these so that a plugin can reliably extend the functionality seems overly heavy handed.

  7. cjacks says:

    Steve – to be clear, we're not blocking functionality, we're just changing the return value from the version check APIs. Add-in developers hosted inside of somebody else's process can reliably use the version helper APIs or find other ways to check for features before enabling. Since this scenario only impacts new code (whether hosted inside of legacy code or not) there are reliable workarounds for this.