Click here to Skip to main content
15,881,803 members
Articles / Desktop Programming / Win32

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

Rate me:
Please Sign up or sign in to vote.
4.73/5 (18 votes)
6 Nov 2013CPOL5 min read 143.4K   41   31
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.

C++
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.

C++
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.

C++
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.

C++
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.

C++
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.

C++
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.

MC++
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.

C++
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
<?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.

C++
#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.

 

C++
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)


Written By
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

Comments and Discussions

 
GeneralThrowing some ideas to the community Pin
SolarNigerija5-Nov-18 10:36
SolarNigerija5-Nov-18 10:36 
QuestionA simple solution that works on Windows 10 and Server 2012 Pin
Rahsas18-Oct-17 4:38
Rahsas18-Oct-17 4:38 
QuestionSimplest solution (if you're lazy to read this article) Pin
Tarmo Pikaro24-Nov-16 10:47
Tarmo Pikaro24-Nov-16 10:47 
SuggestionAnother way Pin
RLebeau10-Aug-15 10:29
RLebeau10-Aug-15 10:29 
SuggestionA much easier and shorter code: Pin
Elmue24-Jun-15 17:06
Elmue24-Jun-15 17:06 
GeneralRe: A much easier and shorter code: Pin
dc_200013-Jul-15 17:26
dc_200013-Jul-15 17:26 
QuestionJust use RtlGetVersion() instead of all this. Pin
User-17514729-Nov-14 11:29
User-17514729-Nov-14 11:29 
AnswerRe: Just use RtlGetVersion() instead of all this. Pin
Ehsan A Samani10-Nov-14 9:40
professionalEhsan A Samani10-Nov-14 9:40 
GeneralRe: Just use RtlGetVersion() instead of all this. Pin
Member 176990911-Nov-14 3:25
Member 176990911-Nov-14 3:25 
GeneralRe: Just use RtlGetVersion() instead of all this. Pin
User-17514725-Jan-15 9:04
User-17514725-Jan-15 9:04 
GeneralRe: Just use RtlGetVersion() instead of all this. Pin
Mosc17-Jun-15 2:47
Mosc17-Jun-15 2:47 
GeneralRe: Just use RtlGetVersion() instead of all this. Pin
User-17514723-Jul-15 23:56
User-17514723-Jul-15 23:56 
QuestionI would suggest using the WMI in stead. Pin
Member 176990921-Oct-14 2:56
Member 176990921-Oct-14 2:56 
AnswerRe: I would suggest using the WMI in stead. Pin
Ehsan A Samani21-Oct-14 3:06
professionalEhsan A Samani21-Oct-14 3:06 
GeneralI wonder... Pin
Cristian Amarie13-Sep-14 3:03
Cristian Amarie13-Sep-14 3:03 
... if RtlGetVersion would bump in the same way. That would be something.
QuestionVersion of Win2000 appears wrong? Pin
John P. Curtis30-Apr-14 12:31
John P. Curtis30-Apr-14 12:31 
AnswerRe: Version of Win2000 appears wrong? Pin
Ehsan A Samani30-Apr-14 15:09
professionalEhsan A Samani30-Apr-14 15:09 
QuestionHow to import an xml file two can? Pin
park suno27-Jan-14 18:33
park suno27-Jan-14 18:33 
QuestionArticle Title? Pin
thatraja8-Dec-13 20:11
professionalthatraja8-Dec-13 20:11 
GeneralWhen will Microsoft learn? Pin
ahmd030-Nov-13 9:20
ahmd030-Nov-13 9:20 
GeneralRe: When will Microsoft learn? Pin
Ehsan A Samani30-Nov-13 9:44
professionalEhsan A Samani30-Nov-13 9:44 
GeneralAnother way Pin
ledtech36-Nov-13 12:16
ledtech36-Nov-13 12:16 
GeneralRe: Another way Pin
Ehsan A Samani6-Nov-13 12:41
professionalEhsan A Samani6-Nov-13 12:41 
GeneralRe: Another way Pin
ledtech36-Nov-13 13:56
ledtech36-Nov-13 13:56 
GeneralMy vote of 5 Pin
Pablo Aliskevicius6-Nov-13 3:49
Pablo Aliskevicius6-Nov-13 3:49 

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

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