If WMI is not an option for you, You could use the SDK counterparts of the GetVersionEx functions, RtlGetVersion. They don't lie.
If you use .NET 5.0 or Core, Environment.OSVersion reports the actual version of Windows.
I posted more detail about this as addition to the answers on this other thread:
https://stackoverflow.com/a/64804643/2240196
EDIT: Added example code as requested. (You probably don't need all of it)
using System;
using System.Runtime.InteropServices;
namespace VersionHelper
{
public static class VersionHelper
{
// The C(++) macro VER_SET_CONDITION mentioned in the documentation for RtlVerifyVersionInfo seems to be equivalent to the VerSetConditionMask function in kernel32.dll
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-versetconditionmask
[DllImport("kernel32.dll")]
private static extern ulong VerSetConditionMask(ulong dwlConditionMask, uint dwTypeBitMask, byte dwConditionMask);
// https://learn.microsoft.com/en-us/windows/win32/devnotes/rtlgetversion
[DllImport("ntdll.dll")]
private static extern int RtlGetVersion(ref OSVERSIONINFOW lpVersionInformation);
[DllImport("ntdll.dll")]
private static extern int RtlGetVersion(ref OSVERSIONINFOEXW lpVersionInformation);
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlverifyversioninfo
[DllImport("ntdll.dll")]
private static extern bool RtlVerifyVersionInfo([In] ref OSVERSIONINFOEXW lpVersionInformation, uint dwTypeMask, ulong dwlConditionMask);
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexw
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct OSVERSIONINFOEXW
{
internal uint dwOSVersionInfoSize;
internal uint dwMajorVersion;
internal uint dwMinorVersion;
internal uint dwBuildNumber;
internal uint dwPlatformId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
internal string szCSDVersion;
internal ushort wServicePackMajor;
internal ushort wServicePackMinor;
internal ushort wSuiteMask;
internal byte wProductType;
internal byte wReserved;
}
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfow
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct OSVERSIONINFOW
{
internal uint dwOSVersionInfoSize;
internal uint dwMajorVersion;
internal uint dwMinorVersion;
internal uint dwBuildNumber;
internal uint dwPlatformId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
internal string szCSDVersion;
}
/*
* RTL_OSVERSIONINFOEX(A/W) and OSVERSIONINFOEX(A/W) are aliases for the same structures
* RTL_OSVERSIONINFO(A/W) and OSVERSIONINFO(A/W) are aliases for the same structures
* */
// These constants initialized with corresponding definitions in
// winnt.h (part of Windows SDK)
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-ver_set_condition
private const byte VER_EQUAL = 1;
private const byte VER_GREATER = 2;
private const byte VER_GREATER_EQUAL = 3;
private const byte VER_LESS = 4;
private const byte VER_LESS_EQUAL = 5;
private const byte VER_AND = 6;
private const byte VER_OR = 7;
private const byte VER_CONDITION_MASK = 7;
private const byte VER_NUM_BITS_PER_CONDITION_MASK = 3;
private const uint STATUS_SUCCESS = 0x00000000;
//
// RtlVerifyVersionInfo() type mask bits
//
private const uint VER_MINORVERSION = 0x0000001;
private const uint VER_MAJORVERSION = 0x0000002;
private const uint VER_BUILDNUMBER = 0x0000004;
private const uint VER_PLATFORMID = 0x0000008;
private const uint VER_SERVICEPACKMINOR = 0x0000010;
private const uint VER_SERVICEPACKMAJOR = 0x0000020;
private const uint VER_SUITENAME = 0x0000040;
private const uint VER_PRODUCT_TYPE = 0x0000080;
// wProductType
// Any additional information about the system.This member can be one of the following values.
private const byte VER_NT_DOMAIN_CONTROLLER = 0x0000002;
private const byte VER_NT_SERVER = 0x0000003;
private const byte VER_NT_WORKSTATION = 0x0000001;
// You can customize this to check for the condition(s) you need using any field from the OSVERSIONINFOW struct with the corresponding VER_ and VER_<operator> constants
public static bool IsWindowsVersionOrGreater(uint majorVersion, uint minorVersion, ushort servicePackMajor = 0, uint buildNumber = 0)
{
var osVerInfo = new OSVERSIONINFOEXW
{
dwOSVersionInfoSize = (uint) Marshal.SizeOf(typeof(OSVERSIONINFOEXW)),
dwMajorVersion = majorVersion,
dwMinorVersion = minorVersion,
wServicePackMajor = servicePackMajor,
dwBuildNumber = buildNumber
};
ulong versionOrGreaterMask = VerSetConditionMask(
VerSetConditionMask(
VerSetConditionMask(
0, VER_MAJORVERSION, VER_GREATER_EQUAL),
VER_MINORVERSION, VER_GREATER_EQUAL),
VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
uint versionOrGreaterTypeMask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR;
if (buildNumber > 0)
{
versionOrGreaterMask = VerSetConditionMask(versionOrGreaterMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
versionOrGreaterTypeMask |= VER_BUILDNUMBER;
}
return RtlVerifyVersionInfo(ref osVerInfo, versionOrGreaterTypeMask, versionOrGreaterMask);
}
public static bool IsWindowsServer()
{
var osVerInfo = new OSVERSIONINFOEXW
{
dwOSVersionInfoSize = (uint) Marshal.SizeOf(typeof(OSVERSIONINFOEXW)),
wProductType = VER_NT_WORKSTATION
};
ulong dwlConditionMask = VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL);
return !RtlVerifyVersionInfo(ref osVerInfo, VER_PRODUCT_TYPE, dwlConditionMask);
}
public static int GetWindowsBuildNumber()
{
var osVerInfo = new OSVERSIONINFOW
{
dwOSVersionInfoSize = (uint) Marshal.SizeOf(typeof(OSVERSIONINFOW))
};
if (STATUS_SUCCESS == RtlGetVersion(ref osVerInfo)) // documented to always return STATUS_SUCCESS
return (int)osVerInfo.dwBuildNumber;
throw new Win32Exception("Failed to determine Windows build number.");
}
// Other functions replicating SDK Version Helper functions
// https://learn.microsoft.com/en-us/windows/win32/sysinfo/version-helper-apis
//
// _WIN32_WINNT version constants
//
const ushort _WIN32_WINNT_NT4 = 0x0400;
const ushort _WIN32_WINNT_WIN2K = 0x0500;
const ushort _WIN32_WINNT_WINXP = 0x0501;
const ushort _WIN32_WINNT_WS03 = 0x0502;
const ushort _WIN32_WINNT_WIN6 = 0x0600;
const ushort _WIN32_WINNT_VISTA = 0x0600;
const ushort _WIN32_WINNT_WS08 = 0x0600;
const ushort _WIN32_WINNT_LONGHORN = 0x0600;
const ushort _WIN32_WINNT_WIN7 = 0x0601;
const ushort _WIN32_WINNT_WIN8 = 0x0602;
const ushort _WIN32_WINNT_WINBLUE = 0x0603;
const ushort _WIN32_WINNT_WINTHRESHOLD = 0x0A00;
const ushort _WIN32_WINNT_WIN10 = 0x0A00;
const bool FALSE = false;
static byte LOBYTE(ushort w)
{
return ((byte)(w & 0xff));
}
static byte HIBYTE(ushort w)
{
return ((byte)(w >> 8 & 0xff));
}
public static bool
IsWindowsXPSP1OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 1);
}
public static bool
IsWindowsXPSP2OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 2);
}
public static bool
IsWindowsXPSP3OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 3);
}
public static bool
IsWindowsVistaOrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0);
}
public static bool
IsWindowsVistaSP1OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 1);
}
public static bool
IsWindowsVistaSP2OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 2);
}
public static bool
IsWindows7OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0);
}
public static bool
IsWindows7SP1OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 1);
}
public static bool
IsWindows8OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0);
}
public static bool
IsWindows8Point1OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0);
}
public static bool
IsWindows10OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN10), LOBYTE(_WIN32_WINNT_WIN10), 0);
}
}
}