使用C#和C++进行Win11版本检测

前言

CodeProject上面找到一篇博文:Windows 11 Version Detection,介绍了使用C#和C++对当前Windows系统的版本进行检测,看是否是Win11系统,特此记录一下。

Window 11版本检测

使用C#和C++进行Windows 11版本检测
在这篇文章中,你将会看到如何使用C#和C++检测Windows 11系统版本。

目录

  • 介绍
  • Windows Vista Fiasco
  • 清单方法
  • RTLGetVersion
  • Windows版本检索黑客
  • 参考
  • 历史
    示例代码托管在 Github 上。

介绍

Windows系统版本
上表没有错误!Windows 11版本确实是10.0。从Windows内核的角度来看,Windows 10和11大致相同。Windows 11命名可能是一个营销决策。

Windows Vista Fiasco

许多为Windows XP编写的程序由于下面的检查而拒绝在Vista上运行。由于Vista版本号是6.0,所以version.Minor将失败,因为 0 既不大于也不等于 1。Windows 7版本是6.1,因此不受其影响。

if (version.Major >= 5 && version.Minor >= 1) 
{
    // WinXP or later: good to go!?
}

更正后的检查如下,但在Vista时代修复Windows XP应用程序为时已晚。

if (version.Major > 5 || 
   (version.Major == 5 && version.Minor >= 1) )
{
    // WinXP or later
}

为了防止历史重演,Microsoft设计了另一种分别基于 .NET Framework 4.8 和 C++ 的 XML 格式清单文件查询 Windows 版本的方法。清单文件嵌入在最终可执行文件中。.NET6 不需要此清单文件来获取正确的 Windows 版本。可悲的是,似乎 .NET Framework 4.8 和C++项目被Microsoft抛在后面,转而支持 .NET6.

Manifest Method

在本部分中,我们将使用以下代码来探索用于查询 Windows 版本的 XML 清单方法。单击“C++”选项卡以查看C++源代码(如果您是C++程序员)。

// C#

using System;

var info = Environment.OSVersion.Version;
Console.WriteLine("Windows Version: {0}.{1}.{2}", 
                  info.Major, info.Minor, info.Build);

// C++

#include <Windows.h>

OSVERSIONINFOW osv;
osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
if (GetVersionExW(&osv))
{
    std::cout << "Windows Version: "
              << osv.dwMajorVersion << "." 
              << osv.dwMinorVersion << "." 
              << osv.dwBuildNumber << "\n";
}

这是上述代码在 Windows 11 计算机上输出的版本。6.2 是 Windows 8.0 的版本。显然,这是不正确的。让我们向项目添加一个清单文件来修复它。

Windows Version: 6.2.9200

在 Visual Studio 解决方案中,右键单击“解决方案资源管理器”中的 C# 项目名称,然后选择“添加”->“新建项”。 出现一个选择对话框,单击General并选择应用程序清单文件仅限 Windows)并输入其文件名 app.manifest
添加应用程序清单文件
对于 Visual C++,微软没有提供添加清单文件的方法。 我们将这样做:将 app.manifest 从 C# 项目复制到 Visual C++ 项目文件夹,并将其重命名为manifest.xml,然后按照以下步骤将此manifest.xml 通知给 Visual C++。 右键单击解决方案资源管理器中的 C++ 项目,然后选择“属性”,然后在弹出的“属性”对话框中单击“清单工具”->“输入和输出”->“其他清单文件”并添加“manifest.xml”。
Visual C++中添加应用程序清单文件
在清单文件中,我们将通过删除周围的 <!----> 来注释 Windows 8.1的supportedOS 字段,并查看其效果。

<!-- Windows 8.1 -->
<!-- <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" /> -->

<!-- Windows 10 -->
<!-- <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> -->

现在代码输出 6.3,即 Windows 8.1,但仍然不正确。 请记住我的机器安装的是 Windows 11。

Windows Version: 6.3.9600

下一步,我们将Window 10的supportedOS 字段注释掉,看看其效果。

Windows Version: 10.0.22000

现在,这是正确的。 Windows 10 和 11 共享相同的主要版本和次要版本,Windows 11 通过其内部版本号 22000 进行区分。清单方法可确保应用程序永远不会获得高于其清单中指定的版本。 接下来,我们将使用 Windows 内核函数 RtlGetVersion 绕过清单文件,始终报告当前的 Windows 版本,而不受清单存在/不存在的影响。

RTLGetVersion

在我们的 GetVersion 中,我们将通过 P/Invoke 调用 RtlGetVersion 并检查内部版本号是否为 22000。如果是,则主版本更新为 11。对于 C++ 程序员,请单击 C++ 选项卡查看 C++ 源代码 。
// C#

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;
        if (osv.dwBuildNumber >= 22000)
            info.Major = 11;
        return true;
    }
    return false;
}

// C++

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;
		if (osv.dwBuildNumber >= 22000)
			info.Major = 11;
		return true;
	}
	return false;
}

下面这是 .NET6 的 GetVersion,其中不需要 Windows 内核函数 RtlGetVersion,因为 .NET6 Environment.OSVersion.Version 在没有 app.manifest 的情况下提供了正确的版本。
// C#

public static void GetVersion(out VersionInfo info)
{
    info.Major = Environment.OSVersion.Version.Major;
    info.Minor = Environment.OSVersion.Version.Minor;
    info.BuildNum = Environment.OSVersion.Version.Build;
    if (info.BuildNum >= 22000)
        info.Major = 11;
}

这就是 GetVersion() 的使用方式。
// C#

VersionInfo info;
if (WinVersion::GetVersion(info))
{
    std::cout << "Windows Version : " 
              << info.Major << "." 
              << info.Minor << "." 
              << info.BuildNum << "\n";
}

// C++

VersionInfo info;
if (WinVersion::GetVersion(info))
{
    std::cout << "Windows Version : " 
              << info.Major << "." 
              << info.Minor << "." 
              << info.BuildNum << "\n";
}

这是我们编辑的 Windows 版本。

Windows Version: 11.0.22000

Windows Version Retrieval Hack

Pavel Yosifovich 的 Windows Native API 编程 一书中提到了一种通过每个进程中位于 0x7ffe0000 地址的 KUSER_SHARED_DATA 结构体获取 Windows 版本的方法。 有关 KUSER_SHARED_DATA 的更多信息,请阅读此 MSDN 链接。 KUSER_SHARED_DATA 位于 Windows 驱动程序开发工具包 (DDK) 附带的 <ntddk.h> 中。 您必须安装 DDK 才能编译下面的代码片段。

// Requires installation of the Windows Driver Development Kit (ddk)
#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;
}

因为我的系统上没有安装DDK。 幸运的是,我在同样由 Pavel Yosifovich 撰写的Windows 10 系统编程,第 1 部分 一书中找到了这段代码片段。 此代码不依赖于 KUSER_SHARED_DATA,而是依赖于其结构成员的地址偏移。 它在我的 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),  // major version offset
        *(ULONG*)(sharedUserData + 0x270),  // minor version offset
        *(ULONG*)(sharedUserData + 0x260)); // build number offset

    return 0;
}

程序输出为 10.0.22621,这在我的 Windows 11 22H2 上是正确的。

Version: 10.0.22621

参考

历史

  • 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

License

本文以及任何相关的源代码和文件均根据The Code Project Open License (CPOL) 获得许可。
撰写者
黄绍文
软件开发人员(高级)
新加坡,新加坡
邵文来自新加坡。 他的兴趣主要在于计算机图形、软件优化、并发性、安全性和敏捷方法。 他的爱好是编写免费的 C++/DirectX 照片幻灯片应用程序,可以在此处查看。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值