应用笔记 / 经验分享 · 2023年3月1日

C#程序获取正确的Windows版本信息—Properly detect Windows version in C# .NET – even Windows 10

在C#中,可以使用System.Environment.OSVersion 相关对象获得当前操作系统的具体信息,但不幸的是,在低于.Net Framework 5.0的框架下,此信息返回的是兼容模式下当前程序集所支持运行的最低系统版本(有点拗口,我的理解就是返回的是你的桌面程序所支持的最小系统版本),而不是当前操作系统的真实版本。

要想解决此问题,最简单的办法是在app.manifest文件中加入兼容列表,如下:

当然还可以通过查询WMI或者使用系统API来实现,有兴趣的朋友可以读全文。

OK, so you are using System.Environment.OSVersion.Version…

The .NET Framework provides a class to find out the version of Windows. Take a look at the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
namespace OsVersionCheck
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"" +
                $"{System.Environment.OSVersion.Version.Major}." +
                $"{System.Environment.OSVersion.Version.Minor}." +
                $"{System.Environment.OSVersion.Version.Build}" +
                $"");
            Console.ReadKey();
        }
    }
}

The output of the above code looks something like this:

6.2.9200

This number can be used to identify the operating system. The following table summarizes the most recent operating system version numbers:

 

Operating system Version number (Major.Minor)
Windows 11 10.0*
Windows 10 10.0*
Windows Server 2022 10.0*
Windows Server 2019 10.0*
Windows Server 2016 10.0*
Windows 8.1 6.3*
Windows Server 2012 R2 6.3*
Windows 8 6.2
Windows Server 2012 6.2
Windows 7 6.1
Windows Server 2008 R2 6.1
Windows Server 2008 6.0
Windows Vista 6.0
Windows Server 2003 R2 5.2
Windows Server 2003 5.2
Windows XP 64-Bit Edition 5.2
Windows XP 5.1
Windows 2000 5.0

However, this does not work as desired if the function is executed on the operating systems marked with an asterisk in the table above. Specifically, these are: Windows 11, Windows 10, Windows Server 2022, Windows Server 2019, Windows Server 2016, Windows 8.1, Windows Server 2012 R2

This means for .NET Framework and .NET Core until version 4.8 respectively 3.1: for Windows 10 it will return 6.2, which is wrong, as this refers to Windows 8 / Windows Server 2012. Note: .NET 5.0+ is not affected and returns the correct version information.

How does this behavior come about? (Note: you can skip the following paragraph if you are not interested in technical background details)

Why OSVersion.Version will return wrong results on newer OSes

In Windows 8.1 and Windows 10, the GetVersion and GetVersionEx functions have been deprecated. In Windows 10, the VerifyVersionInfo function has also been deprecated. While you can still call the deprecated functions, if your application does not specifically target Windows 8.1 or Windows 10, you will get Windows 8 version (6.2.0.0).

https://docs.microsoft.com/de-de/windows/win32/sysinfo/targeting-your-application-at-windows-8-1

In order to target Windows 8.1 or Windows 10, you need to include the app manifest in the source file, which is our first possible solution.

Targeting your application for Windows in the app manifest

In Visual Studio you can add an app manifest by doing the following:

Go to your project, right click and choose Add / New Item and choose Application Manifest File. A new file will be added to your project having default name app.manifest.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
  <application>
    <!-- A list of the Windows versions that this application has been tested on
         and is designed to work with. Uncomment the appropriate elements
         and Windows will automatically select the most compatible environment. -->
          <!-- Windows 10 and Windows 11 -->
          <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
          <!-- Windows 8.1 -->
          <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
          <!-- Windows 8 -->
          <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
          <!-- Windows 7 -->
          <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
          <!-- Windows Vista -->
          <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
      </application>
  </application>
</compatibility>

If you run the program again, the output on a Windows 10 will be correct now:

10.0.17763

Please note:

  • Microsoft says: Manifesting the .exe for Windows 8.1 or Windows 10 will not have any impact when run on previous operating systems.
  • The GUIDs in manifest file are commented with Windows desktop versions, but they are also valid for the Windows Server edition. E.g., the GUID for Windows 8.1 also represents Windows Server 2012 R2, as they have the same OS version number 6.3
  • The approach with the manifest file has a major drawback: Every time Microsoft launches a new Windows version, you need to update your application with a new manifest file. Without such an update, your app will not be able to detect the new Windows and will report the number wrong (again).

Calling RtlGetVersion in ntdll.dll

The Windows Kernel offers an interesting function. The RtlGetVersion routine returns version information about the currently running operating system. It is available starting with Windows 2000 and also works on Windows 10/Server 2019/Server 2016 right away.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace ConsoleApp1
{
    class Program
    {
        /// <summary>
        /// taken from https://stackoverflow.com/a/49641055
        /// </summary>
        /// <param name="versionInfo"></param>
        /// <returns></returns>
        [SecurityCritical]
        [DllImport("ntdll.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern int RtlGetVersion(ref OSVERSIONINFOEX versionInfo);
        [StructLayout(LayoutKind.Sequential)]
        internal struct OSVERSIONINFOEX
        {
            // The OSVersionInfoSize field must be set to Marshal.SizeOf(typeof(OSVERSIONINFOEX))
            internal int OSVersionInfoSize;
            internal int MajorVersion;
            internal int MinorVersion;
            internal int BuildNumber;
            internal int PlatformId;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
            internal string CSDVersion;
            internal ushort ServicePackMajor;
            internal ushort ServicePackMinor;
            internal short SuiteMask;
            internal byte ProductType;
            internal byte Reserved;
        }
        static void Main(string[] args)
        {
            var osVersionInfo = new OSVERSIONINFOEX { OSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)) };
            if (RtlGetVersion(ref osVersionInfo) != 0)
            {
                // TODO: Error handling
            }
            Console.WriteLine($"Windows Version {osVersionInfo.MajorVersion}.{osVersionInfo.MinorVersion}.{osVersionInfo.BuildNumber}");
            Console.ReadKey();
        }
    }
}

How to get the Windows release ID (like “21H2”) and the update build release (UBR)

Feature updates for Windows 10 are released twice a year, around March and September, via the Semi-Annual Channel. They will be serviced with monthly quality updates for 18 or 30 months from the date of the release, depending on the lifecycle policy.

As a result, Windows 10 has the feature update versions, which are 1909, 1903, 1809, etc. This is referred as Release ID. Within this feature release, Windows gets monthly quality updates.

The current state of this quality updates can be determined using the UBR, which is the last part of the build number (the 778 in “Version 10.0.18363.778”). According to current knowledge, both information can only be read from the registry and there is no Windows API command for this. This is also confirmed by the fact that WINVER also uses the registry.

until Windows 10 version 2009/20H2 (build 19042) later Windows versions
Windows release HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ReleaseID HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DisplayVersion
update build release (UBR) HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UBR HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UBR
Windows registry keys for Windows Release ID / Display Version and UBR