前言
在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 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”。
在清单文件中,我们将通过删除周围的 <!--
和 -->
来注释 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
参考
- Wikipedia: List of Microsoft Windows versions
- MSDN Q&A: Win32 API to detect Windows 11
- MSDN Q&A: SupportedOS ID for windows 11
- Pluralsight: Windows 11 Internals: Foundations by Pavel Yosifovich
历史
- 30th July, 2023: Added Windows Version Retrieval Hack section
- 20th April, 2023: Fixed
IsBuildNumGreaterOrEqual()
bug and added Windows -BuildNumber enum
forIsBuildNumGreaterOrEqual()
- 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 照片幻灯片应用程序,可以在此处查看。