Click here to Skip to main content
Click here to Skip to main content

Part1: Overcoming Windows 8.1's deprecation of GetVersionEx and GetVersion APIs

, 6 Nov 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
GetWindows Version on Windows 8.1

Introduction

Starting With Windows 8.1 you can no longer use a block of code that looks like this.

OSVERSIONINFOEX  versionInfo;
   versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
OSVERSIONINFO*  pVersionInfo = (POSVERSIONINFO)&versionInfo;   
if(::GetVersionEx(pVersionInfo))
{
  wcout<<_T("MajorVersion:")<<versionInfo.dwMajorVersion<<endl;
  wcout<<_T("MinorVersion:")<<versionInfo.dwMinorVersion<<endl;
}

Please Look Update Section for better ways to achieve this.

Background

I recently upgraded my development box to Windows8.1 from Windows 8. I was trying to log the version of Windows to the product’s logfile and suddenly I am faced with an issue where the Windows Version I retrieve from GetVersionEx API is actually associated with Windows 8. That is I expected Version 6.3 = Windows8.1 but I was getting Version 6.2 = Windows8. It was impossible to believe such an important API would be malfunctioning, so using Hyper-V I created a Windows8.1 Image from scratch to make sure this was not an issue associated with Windows Upgrade. But the issue persisted, so I upgraded to Visual Studio 2013, hoping that would solve the issue. The issue persisted and that’s when I found the following Article on msdn describing Microsoft’s deprecation of GetVersionEx API. Actually Visual 2013 Marks GetVersionEx and GetVersion APIs as deprecated and throws an Error if you reference it. Moreover .Net Framework’s API to retrieve OS version is also “deprecated” (returning inaccurate data). Let’s just say this move is a big surprise by Microsoft. How is a Web Browser Supposed to Report an accurate User Agent? how is a product supposed to report accurate Telemetry Data without obtaining Operating System version. In My particular case, how am I supposed to log the version of OS in Diagnostic log files that customer would be shipping to me? All these Questions led me to believe that Microsoft's Version Helper API that Ships with Windows 8.1 SDK are not sufficient. But they do provide a very good clue that VerifyVersionInfo can indeed be used to fill the void.

I decided against doing dynamic LoadLibrary and GetProcAddress Technique to use "undocumented" from Kernelbase.dll,Kernel32.dll, shell32.dll or shcore.dll and using internal APIs there. It also seemed not the best idea to load the version of Windows from someplace in Registry. Another approach would have been to load any key dll that resides in %systemroot%\ system32 folder and then use an API such as DllGetVersion to retrieve Product Version from that DLL. but I did not like that idea either because a dll itself and can get hotfixed and potentially have different version (I have not seen this occur).

Using the code

Disclaimer: This is not Production-ready code!

If you look close at VerifyVersionInfo and its sister API VerSetConditionMask there is useful flag called VER_EQUAL.

We can verify the System is Running Windows 8.1 by using VER_EQUAL FLAG.

BOOL EqualsMajorVersion(DWORD majorVersion)
{
    OSVERSIONINFOEX osVersionInfo;
    ::ZeroMemory(&osVersionInfo, sizeof(OSVERSIONINFOEX));
    osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osVersionInfo.dwMajorVersion = majorVersion;
    ULONGLONG maskCondition = ::VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
    return ::VerifyVersionInfo(&osVersionInfo, VER_MAJORVERSION, maskCondition);
}
BOOL EqualsMinorVersion(DWORD minorVersion)
{
    OSVERSIONINFOEX osVersionInfo;
    ::ZeroMemory(&osVersionInfo, sizeof(OSVERSIONINFOEX));
    osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osVersionInfo.dwMinorVersion = minorVersion;
    ULONGLONG maskCondition = ::VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
    return ::VerifyVersionInfo(&osVersionInfo, VER_MINORVERSION, maskCondition);
}

int _tmain(int argc, _TCHAR* argv[])
{
    if (EqualsMajorVersion(6) && EqualsMinorVersion(3))
    {
        wcout << _T("System is Windows 8.1");
    }
    return 0;
}

That Works and in fact that's very similar to the Mechanism used by VersionHelpers.h From Windows 8.1 SDK.

ERSIONHELPERAPI
IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
{
    OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
    DWORDLONG        const dwlConditionMask = VerSetConditionMask(
        VerSetConditionMask(
        VerSetConditionMask(
            0, VER_MAJORVERSION, VER_GREATER_EQUAL),
               VER_MINORVERSION, VER_GREATER_EQUAL),
               VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);

    osvi.dwMajorVersion = wMajorVersion;
    osvi.dwMinorVersion = wMinorVersion;
    osvi.wServicePackMajor = wServicePackMajor;

    return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | 
      VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
}

So You might have noticed Windows Versions have been historically predictable. By That I mean we had Windows 3.0, Windows 3.1, Windows NT, 95(4.0), Windows 2000 (5.0), Windows XP (5.1), .... Windows 7(6.1), ..... , Windows 8.1 (6.3). It's not like previous Version was 100.0 and the next version is 213.0. so it fair to say with a small number of iterations and incrementing of a base known version we should able to get the version of windows that we are running on.

So Here are three More Helper Functions to Get ServicePack and ascertain whether this Windows is Workstation or Windows Server.

BOOL EqualsServicePack(WORD servicePackMajor)
{
    OSVERSIONINFOEX osVersionInfo;
    ::ZeroMemory(&osVersionInfo, sizeof(OSVERSIONINFOEX));
    osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osVersionInfo.wServicePackMajor = servicePackMajor;
    ULONGLONG maskCondition = ::VerSetConditionMask(0, VER_SERVICEPACKMAJOR, VER_EQUAL);
    return ::VerifyVersionInfo(&osVersionInfo, VER_SERVICEPACKMAJOR, maskCondition);
}

BOOL EqualsProductType(BYTE productType)
{
    OSVERSIONINFOEX osVersionInfo;
    ::ZeroMemory(&osVersionInfo, sizeof(OSVERSIONINFOEX));
    osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osVersionInfo.wProductType = productType;
    ULONGLONG maskCondition = ::VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL);
    return ::VerifyVersionInfo(&osVersionInfo, VER_PRODUCT_TYPE, maskCondition);
}

BYTE GetProductType()
{
    if (EqualsProductType(VER_NT_WORKSTATION))
    {
        return VER_NT_WORKSTATION;
    }
    else if (EqualsProductType(VER_NT_SERVER))
    {
        return VER_NT_SERVER;
    }
    else if (EqualsProductType(VER_NT_DOMAIN_CONTROLLER))
    {
        return VER_NT_DOMAIN_CONTROLLER;
    }
    return 0;//unkown
}  

VerifyVersionInfo API was relased on Windows 2000 (Windows 5.0) so we can setup an array that contains known version of Windows back to Windows 200 and use this API to verify them.

struct WindowsNTOSInfo
{
    DWORD dwMajorVersion;
    DWORD dwMinorVersion;
    WORD wServicePackMajor;    
    //const TCHAR * pcszOSDisplayName;
};

struct WindowsNTOSInfoEx :WindowsNTOSInfo
{
    BYTE ProductType;
};

const WindowsNTOSInfo KnownVersionsOfWindows[] =
{
    { 6, 3, 0,   },//win8.1,server2012 r2
    { 6, 2, 0,   },//win8,server2012

    { 6, 1, 1,  },//win7,win2008r2 sp1
    { 6, 1, 0,  },//win7,win2008r2

    { 5, 1, 3,  },//winxp sp3
    { 5, 1, 2,  },//winxp sp2
    { 5, 1, 1,  },//winxp sp1
    { 5, 1, 0,  },//winxp

    { 6, 0, 2,  },//WinVista,server2008 SP2
    { 6, 0, 1,  },//WinVista,Server2008 Sp1
    { 6, 0, 0,  },//WinVista,Server2008

    { 5, 2, 2,  },//Windows Server 2003 Sp2
    { 5, 2, 1,  },//Windows Server 2003 Sp1
    { 5, 2, 0,  },//Windows Server 2003


    { 5, 1, 4,  }, //Windows Server 2000 Sp4
    { 5, 1, 3,  }, //Windows Server 2000 Sp3
    { 5, 1, 2,  }, //Windows Server 2000 Sp2
    { 5, 1, 2,  }, //Windows Server 2000 Sp1
    { 5, 1, 0,  }, //Windows Server 2000
};

const size_t n_KnownVersionofWindows = sizeof(KnownVersionsOfWindows) / sizeof(WindowsNTOSInfo);

So Here the Code that retrieves a known Version of Windows.

bool GetKnownWindowsVersion(WindowsNTOSInfoEx& osInfo)
{
    for (size_t i = 0; i < n_KnownVersionofWindows; i++)
    {
        if (EqualsMajorVersion(KnownVersionsOfWindows[i].dwMajorVersion))
        {
            if (EqualsMinorVersion(KnownVersionsOfWindows[i].dwMinorVersion))
            {
                if (EqualsServicePack(KnownVersionsOfWindows[i].wServicePackMajor))
                {
                    osInfo.dwMajorVersion = KnownVersionsOfWindows[i].dwMajorVersion;
                    osInfo.dwMinorVersion = KnownVersionsOfWindows[i].dwMinorVersion;
                    osInfo.wServicePackMajor = KnownVersionsOfWindows[i].wServicePackMajor;
                    osInfo.ProductType = GetProductType();
                    return true;
                }
            }
        }
    }
    return false;
}

But What about checking unkown version of Windows?

well for that we need to write a few loops and call our helper functions to Verify Major,Minor and Service Pack.

const DWORD MajorVersion_Start = 6;
const DWORD MinorVersion_Start = 3;
const WORD ServicePackVersion_Start = 1;

const DWORD MajorVersion_Max = 10;
const DWORD MinorVersion_Max = 5;
const WORD ServicePackVersion_Max = 4;

bool GetUnkownVersion(WindowsNTOSInfoEx& osInfo)
{
    DWORD minorVersionCounterSeed = MinorVersion_Start;
    DWORD servicePackCounterSeed = ServicePackVersion_Start;
    //by design, if we can't find the right service pack we will return true;
    for (DWORD majorVersion = MajorVersion_Start; majorVersion <= MajorVersion_Max; majorVersion++)
    {
        if (EqualsMajorVersion(majorVersion))
        {
            for (DWORD minorVersion = minorVersionCounterSeed; 
                    minorVersion <= MinorVersion_Max; minorVersion++)
            {
                if (EqualsMinorVersion(minorVersion))
                {
                    osInfo.dwMajorVersion = majorVersion;
                    osInfo.dwMinorVersion = minorVersion;
                    osInfo.ProductType = GetProductType();
                    for (WORD servicePack = servicePackCounterSeed; 
                           servicePack <= ServicePackVersion_Max; servicePack++)
                    {
                        if (EqualsServicePack(servicePack))
                        {                            
                            osInfo.wServicePackMajor = servicePack;
                            break;
                        }
                    }
                    return true;
                }
                else
                {
                    //reset servicepack version counter to 0 because
                    //we are going to increment our minor version.
                    servicePackCounterSeed = 0;
                }                
            }
        }
        else
        {
            //reset minor version to start from 0 because we are going to increment majorVersion;
            minorVersionCounterSeed = 0;
        }
    }
    return false;
}

So then let's just call the before mentioned code with a function to glue them together.

bool GetWindowsVersion(WindowsNTOSInfoEx& osInfo)
{
    bool capturedWinVersion = GetKnownWindowsVersion(osInfo);
    if (!capturedWinVersion)
    {
        return GetUnkownVersion(osInfo);
    }
    return capturedWinVersion;
}



int _tmain(int argc, _TCHAR* argv[])
{
    WindowsNTOSInfoEx osInfo;
    ::ZeroMemory(&osInfo,sizeof(WindowsNTOSInfoEx));
    if (GetWindowsVersion(osInfo))
    {
        wcout << _T("MajorVersion:") << osInfo.dwMajorVersion << 
            _T(" VersionMinor:") << osInfo.dwMinorVersion << 
            _T(" ServicePackMajor:") << osInfo.wServicePackMajor << 
            _T(" ProductType:") << osInfo.ProductType << endl;
    }
    else
    {
        wcout << _T("Failed to Detect Windows Version") << endl;
    }
    return 0;
}

Note:I have only smoke tested this code on Windows 8.1 and Windows XP SP3.

Points of Interest

Performance: This code is actually quite slow!

It can take anywhere from 5-15 Milliseconds on a Physical System. It took upward of 30 Milliseconds on a Virtual Machine to Execute. Since This API can be can called many times during an Application's life cycle I have included a High Resolution Timer code to measure how lengthy of an operation this is. It appears Calling VerifyVersionInfo is much more expensive the first time. The cost of subsequent calls appear to be significantly less than the First Call.

BuildNumber: I have not seen people attach significant importance to the BuildNumber of Windows and with this approach retrieving BuildNumber can become really expensive. so I skipped over BuildNumber.

I will include another article to fetch Windows Version. But that approach uses COM and may not be desired by some.

 Update:

A User on this board has provided a better way to do this.

if you include an embeded manifest file as part of your .exe program.  GetVersionEx will return the right version on Windows8.1. Note that you can support other operating systems as well. but you must make sure Windows 8.1 is inclucded.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!--This Id value indicates the application supports Windows 8.1 functionality-->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
      
    </application>
  </compatibility>

  <assemblyIdentity type="win32"
                    name="SampleCode.GetWindowsVersionInfo"
                    version="1.0.0.0"
                    processorArchitecture="x86"
                    publicKeyToken="0000000000000000"
  />
</assembly>

The Great thing about this approach is that the performance  of GetVersionEx is fantastic. 

But the issue still remains that your binaries will always missbehave on Newer Version of Windows (since you don't know newever Version of Windows Guid ahead of time).

Also One More issue is that you can not create an application that runs in Compatiblity Mode with previous Windows OS.

you will have to get rid of Visual Studio Deprecation Errors by disabling by doing something like this.

	#pragma warning (disable : 4996)	
	versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
	POSVERSIONINFO pVersionInfo = (POSVERSIONINFO)&versionInfo;
	if (::GetVersionEx(pVersionInfo))
	{
		if (6 == versionInfo.dwMajorVersion && 3 == versionInfo.dwMinorVersion)
		{
			wcout << _T("Windows 8.1 Detected") << endl;
		}
		else if (6 == versionInfo.dwMajorVersion && 2 == versionInfo.dwMinorVersion)
		{
			wcout << _T("Windows 8.0 Detected") << endl;
		}		
	}
	#pragma warning (default : 4996)

 

I have also discovered another API that still returns Windows Version without any compatability Manifests required.  But it takes a 2-3 miliseconds to execute so it is significantly slower than GetVersionEx API.

 

bool GetWindowsVersion(DWORD& major, DWORD& minor)
{	
	LPBYTE pinfoRawData;
	if (NERR_Success == NetWkstaGetInfo(NULL, 100, &pinfoRawData))
	{
		WKSTA_INFO_100 * pworkstationInfo = (WKSTA_INFO_100 *)pinfoRawData;
		major = pworkstationInfo->wki100_ver_major;
		minor = pworkstationInfo->wki100_ver_minor;
		::NetApiBufferFree(pinfoRawData);
		return true;
	}
	return false;
}
int _tmain(int argc, _TCHAR* argv[])
{	
	DWORD major = 0;
	DWORD minor = 0;
	if (GetWindowsVersion(major, minor))
	{
		wcout << _T("Major:") << major << _T("Minor:") << minor << endl;
	}	
	return 0;
}

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ehsan A Samani
CEO NanoToolkit, Inc
United States United States
I have been doing some sort of Software Development since 1998. I started with C Programming but quickly upgraded to C++. I am proficient in Windows UI, Windows Server/Service, COM, C# and WinForms, C# and Asp.net. I can also claim some know-how in VB.NET and java. Every once in a while I hone in my scripting skills by doing more JQuery and Powershell but I quickly forget it all. I also have a working knowledge of CSS and HTML as well.
check out my Blog
Follow on   LinkedIn

Comments and Discussions

 
QuestionJust use RtlGetVersion() instead of all this. PinmemberUlderico9-Nov-14 12:29 
AnswerRe: Just use RtlGetVersion() instead of all this. PinprofessionalEhsan A Samani10-Nov-14 10:40 
GeneralRe: Just use RtlGetVersion() instead of all this. PinmemberMember 176990911-Nov-14 4:25 
QuestionI would suggest using the WMI in stead. PinmemberMember 176990921-Oct-14 3:56 
AnswerRe: I would suggest using the WMI in stead. PinprofessionalEhsan A Samani21-Oct-14 4:06 
GeneralI wonder... PinmemberCristian Amarie13-Sep-14 4:03 
QuestionVersion of Win2000 appears wrong? PinmemberJohn P. Curtis30-Apr-14 13:31 
AnswerRe: Version of Win2000 appears wrong? PinprofessionalEhsan A Samani30-Apr-14 16:09 
QuestionHow to import an xml file two can? Pinmemberpark suno27-Jan-14 19:33 
QuestionArticle Title? Pinmvpthatraja8-Dec-13 21:11 
I think Part 1 & Part 2 should be suffix(instead of prefix) like below...?
 
Overcoming Windows 8.1's deprecation of GetVersionEx and GetVersion APIs : Part 1[^]
Overcoming Windows 8.1's Deprecation of GetVersionEx and GetVersion APIs : Part 2[^]
thatraja
Code converters | Education Needed
No thanks, I am all stocked up. - Luc Pattyn
When you're wrestling a gorilla, you don't stop when you're tired, you stop when the gorilla is - Henry Minute


GeneralWhen will Microsoft learn? Pinmemberahmd030-Nov-13 10:20 
GeneralRe: When will Microsoft learn? PinprofessionalEhsan A Samani30-Nov-13 10:44 
GeneralAnother way Pinmemberledtech36-Nov-13 13:16 
GeneralRe: Another way PinprofessionalEhsan A Samani6-Nov-13 13:41 
GeneralRe: Another way Pinmemberledtech36-Nov-13 14:56 
GeneralMy vote of 5 PinmemberPablo Aliskevicius6-Nov-13 4:49 
QuestionIt's way easier PinmemberRene Koenig5-Nov-13 23:47 
AnswerRe: It's way easier PinprofessionalEhsan A Samani6-Nov-13 7:43 
GeneralRe: It's way easier PinmemberRene Koenig6-Nov-13 14:11 
GeneralRe: It's way easier PinprofessionalEhsan A Samani6-Nov-13 15:00 
GeneralRe: It's way easier PinprofessionalEhsan A Samani6-Nov-13 17:27 
GeneralMy vote of 5 PinmemberLeandro Taset5-Nov-13 16:02 
QuestionWell Done PinmemberBlake Miller5-Nov-13 12:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141220.1 | Last Updated 7 Nov 2013
Article Copyright 2013 by Ehsan A Samani
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid