.NET 的版本辅助 API






4.93/5 (11投票s)
.NET 的版本辅助 API – 无需 Environment.OSVersion 或应用程序清单即可检查 Windows 8.1 或 Windows Server 2012 R2 的版本
引言
对于 Windows 8.1 和 Windows Server 2012 R2,Environment.OSVersion
(http://msdn.microsoft.com/library/system.environment.osversion.aspx)属性的行为已发生更改。如果未在应用程序清单中明确声明对 Windows 8.1 的支持,则此属性将返回不正确的版本号:6.2,而正确的值应为 6.3。本文提出的 OSVersionHelper
类提供了版本辅助函数的功能,可以解决此问题,该类允许将正在运行的操作系统的版本与已知值进行比较。
背景
Environment.OSVersion
属性中的此更改是由 Windows API 中的 GetVersionEx
(http://msdn.microsoft.com/library/windows/desktop/ms724451.aspx)函数引起的。如 MSDN 文章“Windows 8.1 中的操作系统版本更改”(http://msdn.microsoft.com/library/windows/desktop/dn302074.aspx)的“表现形式”部分所述,如果没有正确的应用程序清单,此函数将为 Windows 8.1 和 Windows Server 2012 R2 返回版本 6.2。此外,在 Windows SDK 8.1(http://msdn.microsoft.com/windows/desktop/bg162891)中,GetVersionEx
函数已被声明为已弃用。构建 C/C++ 代码会针对使用此函数的每次调用产生警告消息 C4996(http://msdn.microsoft.com/library/ttcz0bys.aspx)。然而,使用 Environment.OSVersion
属性的 .NET 代码的构建不会产生警告。在撰写本文时,Environment.OSVersion
的 MSDN 页面并未说明此属性使用已弃用的函数,并且可以使用应用程序清单检索正确的版本号。然而,无法提前为将来的 Windows 版本创建应用程序清单。因此,继续使用 Environment.OSVersion
属性需要每次发布新 Windows 版本后,使用新清单重新构建应用程序。
其他一些作者已经针对 C/C++ 解决了这个问题。例如,Ehsan A Samani 在其文章“第一部分:克服 Windows 8.1 对 GetVersionEx
和 GetVersion API 的弃用”(https://codeproject.org.cn/Articles/678606/Part1-Overcoming-Windows-8-1s-deprecation-of-GetVe)中描述了弃用 GetVersionEx
函数的一些缺点。
作为解决方案,Windows SDK 8.1 提供了版本辅助函数(http://msdn.microsoft.com/library/windows/desktop/dn424972.aspx),相应的 MSDN 文章称这些函数“取代了 GetVersion
和 GetVersionEx
”。这种说法不正确,因为版本辅助函数允许比较已知的 Windows 版本,但例如,它们不提供比较操作系统内部版本号或检查操作系统是否为 Windows Embedded 的能力。也无法检查正在运行的操作系统是否不大于指定的一个。
版本辅助函数的另一个文档问题是其被错误地隐含描述为 Kernel32.lib、ntdll.lib、Kernel32.dll 或 ntdll.dll 的一部分。所有这些函数都在 Windows SDK 8.1 的“VersionHelpers.h”头文件中用 `__forceinline` 关键字定义和声明。这意味着版本辅助函数仅为包含“VersionHelpers.h”文件的 C/C++ 代码定义。此外,如果调用了这些函数或存在指向这些函数的指针,则这些函数将被放置在最终的二进制文件中。
Using the Code
对于 .NET,Microsoft 没有为版本辅助函数提供任何接口。这是合理的,因为这些函数不是由 Kernel32.dll 或 ntdll.dll 导出的,无法通过 PInvoke 调用。然而,本文提供了一个静态类 OSVersionHelper
,它提供与版本辅助函数相同的功能,并附带源代码。
该类包含版本辅助函数的自定义实现。它具有 `VerSetConditionMask`(http://msdn.microsoft.com/library/windows/desktop/ms725493.aspx)和 `VerifyVersionInfo`(http://msdn.microsoft.com/library/windows/desktop/ms725492.aspx)函数的 PInvoke 原型。这两个函数足以实现所有版本辅助函数。这些函数的原型如下:
[DllImport("kernel32.dll")]
static extern ulong VerSetConditionMask(ulong dwlConditionMask,
uint dwTypeBitMask, byte dwConditionMask);
[DllImport("kernel32.dll")]
static extern bool VerifyVersionInfo(
[In] ref OsVersionInfoEx lpVersionInfo,
uint dwTypeMask, ulong dwlConditionMask);
VerifyVersionInfo
函数的第一个参数是 OsVersionInfoEx
结构,它是 OSVERSIONINFOEX
(http://msdn.microsoft.com/library/windows/desktop/ms724833.aspx)结构的一个 .NET 包装器,该结构定义在“Winnt.h”中。此数据结构定义如下:
[StructLayout(LayoutKind.Sequential)]
struct OsVersionInfoEx
{
public uint OSVersionInfoSize;
public uint MajorVersion;
public uint MinorVersion;
public uint BuildNumber;
public uint PlatformId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string CSDVersion;
public ushort ServicePackMajor;
public ushort ServicePackMinor;
public ushort SuiteMask;
public byte ProductType;
public byte Reserved;
}
正在运行的操作系统的版本比较是通过以下方法实现的:
static bool IsWindowsVersionOrGreater(
uint majorVersion, uint minorVersion, ushort servicePackMajor)
{
OsVersionInfoEx osvi = new OsVersionInfoEx();
osvi.OSVersionInfoSize = (uint)Marshal.SizeOf(osvi);
osvi.MajorVersion = majorVersion;
osvi.MinorVersion = minorVersion;
osvi.ServicePackMajor = servicePackMajor;
// These constants initialized with corresponding definitions in
// winnt.h (part of Windows SDK)
const uint VER_MINORVERSION = 0x0000001;
const uint VER_MAJORVERSION = 0x0000002;
const uint VER_SERVICEPACKMAJOR = 0x0000020;
const byte VER_GREATER_EQUAL = 3;
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;
return VerifyVersionInfo(ref osvi, versionOrGreaterTypeMask.Value,
versionOrGreaterMask.Value);
}
检查正在运行的操作系统是否为服务器是通过以下属性实现的:
static bool IsWindowsServer
{
get
{
// These constants initialized with corresponding
// definitions in winnt.h (part of Windows SDK)
const byte VER_NT_WORKSTATION = 0x0000001;
const uint VER_PRODUCT_TYPE = 0x0000080;
const byte VER_EQUAL = 1;
OsVersionInfoEx osvi = new OsVersionInfoEx();
osvi.OSVersionInfoSize = (uint)Marshal.SizeOf(osvi);
osvi.ProductType = VER_NT_WORKSTATION;
ulong dwlConditionMask = VerSetConditionMask(
0, VER_PRODUCT_TYPE, VER_EQUAL);
return !VerifyVersionInfo(
ref osvi, VER_PRODUCT_TYPE, dwlConditionMask);
}
}
本文提供了包含详尽注释的完整源代码,包括用法示例(OSVersion.zip)。示例包括调用 Environment.OSVersion
属性,仅用于打印需要应用程序清单的正在运行操作系统的参考版本。请注意,即使没有应用程序清单,OSVersionHelper
类成员也能在 Windows 8.1 或 Windows Server 2012 R2 上正常工作。
关注点
`VerSetConditionMask` 和 `VerifyVersionInfo` 函数的原型以及 `OSVERSIONINFOEX` 结构的 .NET 包装器可以在 www.pinvoke.net 上的以下页面找到:
- http://www.pinvoke.net/default.aspx/kernel32/VerSetConditionMask.html
- http://www.pinvoke.net/default.aspx/kernel32/VerifyVersionInfo.html
- http://www.pinvoke.net/default.aspx/Structures/OSVERSIONINFOEX.html
在未来的版本中,OSVersionHelper
类可以得到改进,以便使用二分查找和 `VerifyVersionInfo` 函数的不同条件掩码来检索正在运行的操作系统的版本。此外,还可以实现其他功能,例如检测 X64 架构或组合正在运行的操作系统的名称。