65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET 的版本辅助 API

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (11投票s)

2014 年 1 月 7 日

CPOL

4分钟阅读

viewsIcon

39340

downloadIcon

484

.NET 的版本辅助 API – 无需 Environment.OSVersion 或应用程序清单即可检查 Windows 8.1 或 Windows Server 2012 R2 的版本

引言

对于 Windows 8.1 和 Windows Server 2012 R2,Environment.OSVersionhttp://msdn.microsoft.com/library/system.environment.osversion.aspx)属性的行为已发生更改。如果未在应用程序清单中明确声明对 Windows 8.1 的支持,则此属性将返回不正确的版本号:6.2,而正确的值应为 6.3。本文提出的 OSVersionHelper 类提供了版本辅助函数的功能,可以解决此问题,该类允许将正在运行的操作系统的版本与已知值进行比较。

背景

Environment.OSVersion 属性中的此更改是由 Windows API 中的 GetVersionExhttp://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 文章称这些函数“取代了 GetVersionGetVersionEx”。这种说法不正确,因为版本辅助函数允许比较已知的 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 结构,它是 OSVERSIONINFOEXhttp://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 上的以下页面找到:

在未来的版本中,OSVersionHelper 类可以得到改进,以便使用二分查找和 `VerifyVersionInfo` 函数的不同条件掩码来检索正在运行的操作系统的版本。此外,还可以实现其他功能,例如检测 X64 架构或组合正在运行的操作系统的名称。

.NET 的版本辅助 API - CodeProject - 代码之家
© . All rights reserved.