In this article, you will see how to detect Windows version with C# and C++.
Table of Contents
The example code is hosted on Github.
Introduction
No mistake in the above table! Windows 11 version is indeed 10.0
. From the Windows kernel perspective, Windows 10 and 11 are largely the same. The Windows 11 naming is perhaps a marketing decision.
Windows Vista Fiasco
Many programs written for Windows XP, refused to run on Vista due to the check below. Vista version is 6.0
in which the version.Minor
check will fail because 0
is neither greater than nor equal to 1
. Windows 7 version is 6.1
so it is not affected by it.
if (version.Major >= 5 && version.Minor >= 1)
{
}
The corrected check is below but was too late to fix the Windows XP applications out there during Vista era.
if (version.Major > 5 ||
(version.Major == 5 && version.Minor >= 1) )
{
}
To prevent history from repeating itself, Microsoft devised another method of querying the Windows version based on an XML-format manifest file for .NET Framework 4.8 and C++ respectively. The manifest file is embedded in the final executable. .NET6 does not need this manifest file to obtain the correct Windows version. Sadly, it does seem .NET Framework 4.8 and C++ projects are left behind by Microsoft in favor of .NET6.
Manifest Method
In this section, we'll use the code below to explore the XML manifest method to query the Windows version. Click on the C++ tab to see C++ source code if you're a C++ programmer.
using System;
var info = Environment.OSVersion.Version;
Console.WriteLine("Windows Version: {0}.{1}.{2}",
info.Major, info.Minor, info.Build);
#include <Windows.h>
OSVERSIONINFOW osv;
osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
if (GetVersionExW(&osv))
{
std::cout << "Windows Version: "
<< osv.dwMajorVersion << "."
<< osv.dwMinorVersion << "."
<< osv.dwBuildNumber << "\n";
}
This is the version outputted by the above code on a Windows 11 machine. 6.2
is the version of Windows 8.0. Clearly, this is incorrect. Let's add a manifest file to the project to fix it.
Windows Version: 6.2.9200
In the Visual Studio solution, right-click on the C# project name in Solution Explorer and select Add->New Item. A selection dialog comes up and click General and select the Application Manifest File (Windows Only) and enter its filename as app.manifest.
For Visual C++, Microsoft did not provide a way to add a manifest file. We'll do this instead: copy the app.manifest from the C# project to the Visual C++ project folder and rename it to manifest.xml and follow these steps to inform Visual C++ of this manifest.xml. Right-click on the C++ project in the Solution Explorer and select Properties and in the Properties Dialog that pops up and click Manifest Tool->Input and Output->Additional Manifest Files and add "manifest.xml".
In the manifest file, we'll uncomment the supportedOS
for Windows 8.1 by deleting the surrounding <!--
and -->
and see its effect.
Now the code outputs 6.3
which is Windows 8.1 which is still incorrect. Remember my machine is installed with Windows 11.
Windows Version: 6.3.9600
Next, we'll uncomment the supportedOS
for Windows 10 to see its effect.
Windows Version: 10.0.22000
Now, this is correct. Windows 10 and 11 share the same major and minor version and Windows 11 is differentiated by its build number, 22000
. The manifest method ensures that the application never gets a version higher than what is specified in its manifest. Next, we'll use a Windows kernel function RtlGetVersion
to bypass the manifest file to always report the current Windows version without affecting by the presence/absence of manifest.
RtlGetVersion
In our GetVersion
, we'll call RtlGetVersion
via P/Invoke. For C++ programmers, please click on the C++ tab to see the C++ source code.
public static bool GetVersion(out VersionInfo info)
{
info.Major = 0;
info.Minor = 0;
info.BuildNum = 0;
OSVERSIONINFOEXW osv = new OSVERSIONINFOEXW();
osv.dwOSVersionInfoSize = 284;
if (RtlGetVersion(out osv) == 0)
{
info.Major = osv.dwMajorVersion;
info.Minor = osv.dwMinorVersion;
info.BuildNum = osv.dwBuildNumber;
return true;
}
return false;
}
bool WinVersion::GetVersion(VersionInfo& info)
{
OSVERSIONINFOEXW osv;
osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
if (RtlGetVersion(&osv) == 0)
{
info.Major = osv.dwMajorVersion;
info.Minor = osv.dwMinorVersion;
info.BuildNum = osv.dwBuildNumber;
return true;
}
return false;
}
This is how the GetVersion()
is used.
if (WinVersion.GetVersion(out var info))
{
Console.WriteLine("Windows Version: {0}.{1}.{2}",
info.Major, info.Minor, info.BuildNum);
Console.ReadKey();
}
VersionInfo info;
if (WinVersion::GetVersion(info))
{
std::cout << "Windows Version : "
<< info.Major << "."
<< info.Minor << "."
<< info.BuildNum << "\n";
}
This is the Windows 11 version.
Windows Version: 10.0.22000
Windows Version Retrieval Hack
In the Windows Native API Programming book by Pavel Yosifovich, it is mentioned a method to get the Windows version through the KUSER_SHARED_DATA
struct located at 0x7ffe0000
address in every process. For more information about KUSER_SHARED_DATA
, read this MSDN link. KUSER_SHARED_DATA
is located in <ntddk.h>
that comes with Windows Driver Development Kit (DDK). You must install the DDK to compile the code snippet below.
#include <ntddk.h>
#include <cstdio>
int main()
{
auto data = (KUSER_SHARED_DATA*)0x7ffe0000;
printf("Version: %d.%d.%d\n",
data->NtMajorVersion, data->NtMinorVersion, data->NtBuildNumber);
return 0;
}
Since I do not have DDK installed on my system. Fortunately, I found this code snippet in Windows 10 System Programming, Part 1 book, also written by same author. This code does not rely on KUSER_SHARED_DATA
but the address offsets to its structure members. It compiles and works fine on my Visual C++ 2022.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <cstdio>
int main()
{
auto sharedUserData = (BYTE*)0x7FFE0000;
printf("Version: %d.%d.%d\n",
*(ULONG*)(sharedUserData + 0x26c), *(ULONG*)(sharedUserData + 0x270), *(ULONG*)(sharedUserData + 0x260));
return 0;
}
The program output is 10.0.22621 which is correct on my Windows 11 22H2.
Version: 10.0.22621
Update: I completed my 32-bit and 64-bit testing of this hack. On Windows XP, Vista, 7, 8 and 8.1, the build number is an invalid zero while it is valid on Windows 10 and 11. Just beware not to rely on the build number on the Windows OS older than 10 if you use this hack.
References
History
- 2nd September, 2023: Remove the Windows 11 build number check to set major version to 11.
- 27th August, 2023: Added testing results of 32-bit and 64-bit Windows OSes to Windows Version Retrieval Hack section
- 30th July, 2023: Added Windows Version Retrieval Hack section
- 20th April, 2023: Fixed
IsBuildNumGreaterOrEqual()
bug and added Windows BuildNumber enum
for IsBuildNumGreaterOrEqual()
- 30th August, 2022: Added
IsBuildNumGreaterOrEqual()
method in the source code for users who like to detect version via build number - 5th July, 2022: Added the corrected Windows XP check
- 3rd July, 2022: First release