返回

检测Windows 11: .NET Framework 与 Windows API 方法

windows

检测 Windows 11:.NET Framework 和 Windows API 方法

识别运行中的 Windows 系统版本,对于应用兼容性处理至关重要。 本文介绍如何通过 .NET Framework 和 Windows API 检测 Windows 11 系统,并解释其中的原理。

.NET Framework 的检测方法

.NET FrameworkSystem.Environment.OSVersion 属性可以提供操作系统的主要版本号和次要版本号,在大部分情况下能够识别 Windows 系统。 对于Windows 10 及其之前版本,其 MajorMinor 属性的值足以判断。但该方法对 Windows 11 有其局限性。 Windows 11 返回的系统版本号依然是10.0,和 Windows 10 相同,这导致我们不能直接用它区分 Windows 10 与 Windows 11。

利用 Windows.Management.OperatingSystem

一个更可靠的方式是通过使用 Windows.Management 命名空间中的 OperatingSystem 类。这个类属于 Windows Runtime API 的一部分,为更细致的操作系统信息提供了支持,并可区分 Windows 10 和 Windows 11。 它提供了一个更为清晰、直接的方式。

操作步骤:

  1. 添加对 Windows.winmd 的引用:需要手动添加到项目中,此文件通常在 %ProgramFiles(x86)%\Windows Kits\10\UnionMetadata\windows.winmd
  2. 编写代码使用 Windows.Management.OperatingSystem.Version 获取详细版本信息,使用 Build 属性即可判断系统版本,比如 22000以上就是Windows 11.
    using System;
    using System.Reflection;
    using System.Runtime.InteropServices;
    
    public class OsVersionDetector
     {
    
      public static void GetVersion() {
          try
         {
    
             var operatingSystemType = Type.GetTypeFromProgID("Windows.Management.OperatingSystem");
    
             if (operatingSystemType != null) {
    
                  var operatingSystemInstance = Activator.CreateInstance(operatingSystemType);
                  var versionProperty = operatingSystemType.GetProperty("Version");
    
                 if (versionProperty!= null) {
    
                  var versionObject = versionProperty.GetValue(operatingSystemInstance,null);
                 if (versionObject!= null) {
                         var versionType = versionObject.GetType();
    
    
                     var buildProperty = versionType.GetProperty("Build");
                     if(buildProperty!= null)
                     {
    
                        var build = (int)buildProperty.GetValue(versionObject, null);
                             Console.WriteLine("Build number: "+build.ToString());
    
                             if (build >= 22000) {
                               Console.WriteLine("Windows 11 Detected.");
                             }
                             else {
                               Console.WriteLine("Not Windows 11, might be an older Windows Version.");
                             }
                         } else
                             {
                             Console.WriteLine("Error: Could not find build number");
                             }
    
                 }  else {
                            Console.WriteLine("Error: Could not find operating system version info.");
                          }
    
    
    
    
    
    
             }else{
    
                     Console.WriteLine("Error: Can not get Windows Runtime API for version number detection");
    
                     }
              }
    
    
       catch(Exception ex) {
          Console.WriteLine("Error in accessing system runtime api for version: "+ ex.Message );
    
       }
    }
     }
    
  
这个方法无需考虑系统的具体版本号,只需检查`Build` 属性。这种方式相对可靠,并能够适应 Windows 版本更新。  

## Windows API 的检测方法

Windows API 提供了一系列函数,帮助开发者获取操作系统信息。`GetVersion` 函数已被弃用, 不应再使用。 但可以利用`RtlGetVersion` API函数替代获取更详细版本信息。

###  利用 `RtlGetVersion`

`RtlGetVersion` API函数返回的是 `RTL_OSVERSIONINFOEX` 结构体。 这是一个内部未导出函数, 故而需要通过动态获取函数地址使用。 `RTL_OSVERSIONINFOEX` 中提供了包括 `MajorVersion`, `MinorVersion` 和 `BuildNumber` 等详细版本信息。

操作步骤:

1. 声明需要的结构体和函数原型:首先,需要在代码中声明 `RTL_OSVERSIONINFOEX` 结构体和 `RtlGetVersion` 函数原型。

  ```csharp
  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  internal struct RTL_OSVERSIONINFOEX
      {
      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;
      }


   [DllImport("ntdll.dll")]
      internal static extern int RtlGetVersion(out RTL_OSVERSIONINFOEX lpVersionInformation);

  ```
  
2.  调用  `RtlGetVersion`  API 并分析版本信息:获取到  `RTL_OSVERSIONINFOEX`  结构体后,可以分析其内部字段,特别关注  `dwMajorVersion`, `dwMinorVersion`, `dwBuildNumber` 。如果  `dwMajorVersion` 是 10,  且  `dwBuildNumber` 大于等于 22000,则判断为 Windows 11。

```csharp
  public static void GetVersionFromAPI() {
      
          RTL_OSVERSIONINFOEX osVersionInfo = new RTL_OSVERSIONINFOEX();
          osVersionInfo.dwOSVersionInfoSize = (uint)Marshal.SizeOf(osVersionInfo);
           int result = RtlGetVersion(out osVersionInfo);

              if (result >= 0)
          {

          Console.WriteLine($"Windows Version {osVersionInfo.dwMajorVersion}.{osVersionInfo.dwMinorVersion}.{osVersionInfo.dwBuildNumber}");
                  if (osVersionInfo.dwMajorVersion == 10 && osVersionInfo.dwBuildNumber >= 22000)
                     {

                          Console.WriteLine("Detected: Windows 11");


                      }else
                  {
                          Console.WriteLine("Not Windows 11, maybe Windows 10 or less");

                   }

           } else
         {
                  Console.WriteLine("Error getting OS version with win32 api RtlGetVersion()");

          }


  }

这个方法可以直接读取操作系统版本信息,对需要兼容较旧系统应用场景很适用,提供了底层操作系统的控制能力。 同时, 由于该方法调用了系统内部API, 务必保证有足够的操作权限,并谨慎处理版本差异情况。

额外建议

使用上述方式识别系统版本后,不要盲目基于操作系统版本号硬编码处理逻辑。尽可能采用特性检测或运行时的条件检查。这能使你的应用程序更加健壮。

在开发过程中,应仔细考虑版本兼容性,尤其是对那些涉及到操作系统底层特性的应用。做好错误处理并准备适当的回退策略是最佳实践。 在Windows 11 发布后,需要及时更新相关的测试和代码来确保其能识别 Windows 11 系统。