检测Windows 10版本

27

我的目标是在跨平台以及不同版本的Windows系统中检测Windows 10。Windows提供了IsWindows10OrGreater()函数解决这个问题,但它存在另一个问题,这个函数在之前的Windows版本中并不存在。

你会发现有无数的博客和stackoverflow问题,关于如何解决这个问题,以及类似getversion等函数返回错误版本的情况。

比如说,在我的电脑上,IsWindows10OrGreater()方法无法编译(我需要安装Win10 SDK),而IsWindowsVersionOrGreater()函数则报告了主要版本号为6

所以,有没有一种明智的多版本解决方案来解决这个问题呢?


2
你为什么不安装Windows 10 SDK呢? - IInspectable
1
嗯...不需要这样做。只需定义_WIN32_WINNT/WINVER来设置最小支持的目标操作系统即可。详见使用Windows头文件。您可以使用Windows 10 SDK来定位Windows Vista。 - IInspectable
1
那么?只需获取Windows 10 SDK并继续进行。为什么您认为这不是一个解决方案呢? - IInspectable
2
@IInspectable 我的代码将在不同的应用程序之间共享,其中一些(开发人员)可能不会添加清单,许多VS 2015安装默认情况下不包括win10 sdk - 这是我发现这个问题的方式。正如我所说,这不是一次性的代码,它是库的一部分,将在不同的应用程序之间共享,其中一些将在Windows 7或8或10下编译,我必须检测10以便分离我的逻辑。 - hg_git
2
@Barmak:它可以编译,但为了返回所需的结果,二进制文件需要相应的清单条目,即:“现在看,我知道Windows 10的存在,并且已经做好了准备。” - IInspectable
显示剩余6条评论
3个回答

59

检索操作系统真实版本的最直接方法是调用RtlGetVersion。它是GetVersionExVerifyVersionInfo调用的基础,但不使用兼容性shims。

您可以使用DDK(通过在内核模式下#include 并链接到NtosKrnl.lib或在用户模式下链接到ntdll.lib),或者像以下片段中一样使用运行时动态链接:

typedef LONG NTSTATUS, *PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)

typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);

RTL_OSVERSIONINFOW GetRealOSVersion() {
    HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
    if (hMod) {
        RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
        if (fxPtr != nullptr) {
            RTL_OSVERSIONINFOW rovi = { 0 };
            rovi.dwOSVersionInfoSize = sizeof(rovi);
            if ( STATUS_SUCCESS == fxPtr(&rovi) ) {
                return rovi;
            }
        }
    }
    RTL_OSVERSIONINFOW rovi = { 0 };
    return rovi;
}

如果您需要额外的信息,您可以传递一个 RTL_OSVERSIONINFOEXW 结构体来代替 RTL_OSVERSIONINFOW 结构体(正确地分配 dwOSVersionInfoSize 成员)。

即使没有附加清单,这也会在 Windows 10 上返回预期结果。


顺便说一下,通常认为基于可用功能而不是操作系统版本提供不同的实现是更好的解决方案。


3
如何检测功能支持? - Cu2S
4
@Cu2S:这取决于功能。如果一个特性被引入作为新的导出项(例如GetDpiForMonitor),那么您可以像上面的代码一样使用运行时动态链接。如果一个特性被添加为COM接口,您可以像往常一样调用QueryInterface。如果一个特性被引入作为函数的新参数/标志,请使用它并观察返回值。 - IInspectable
1
链接时使用 ntdll.lib 而不是 NtosKrnl.lib。而且 不使用兼容性 shims 不是真的 - 如果我们在文件属性中选择兼容选项卡 - 在兼容模式下运行,RtlGetVersion 将受到影响。 - RbMm
@RbMm:文档与您的说法不符。如果您主动选择在兼容模式下运行应用程序,显然您确实希望应用程序看到不同的操作系统版本。这与为GetVersionEx和相关函数引入的兼容性层不同。 - IInspectable
1
微软文档中又出现了一个“bug”:我发现RTL_OSVERSIONINFOEXWwinnt.h中可用,因此无需获取DDK或手动声明该结构! - gog
显示剩余3条评论

1
您可以从注册表中读取真实的构建号,然后推断出Windows版本。您的应用程序不需要具有清单文件才能正常工作:在我的计算机上,它正确地检测到操作系统构建号为10586。例如:
#include <Windows.h>
#include <sstream>

struct HKeyHolder
{
private:
    HKEY m_Key;

public:
    HKeyHolder() :
        m_Key(nullptr)
    {
    }

    HKeyHolder(const HKeyHolder&) = delete;
    HKeyHolder& operator=(const HKeyHolder&) = delete;

    ~HKeyHolder()
    {
        if (m_Key != nullptr)
            RegCloseKey(m_Key);
    }

    operator HKEY() const
    {
        return m_Key;
    }

    HKEY* operator&()
    {
        return &m_Key;
    }
};

bool IsRunningOnWindows10()
{
    HKeyHolder currentVersion;
    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", 0, KEY_QUERY_VALUE, &currentVersion) != ERROR_SUCCESS)
        return false;

    DWORD valueType;
    BYTE buffer[256];
    DWORD bufferSize = 256;

    if (RegQueryValueExW(currentVersion, L"CurrentBuild", nullptr, &valueType, buffer, &bufferSize) != ERROR_SUCCESS)
        return false;

    if (valueType != REG_SZ)
        return false;

    int version;
    std::wistringstream versionStream(reinterpret_cast<wchar_t*>(buffer));
    versionStream >> version;

    return version > 9800;
}

11
注册表不是公共编程接口。除非该键已正式记录在文档中,否则您的代码依赖于实现细节,这些细节可能随时更改,而无需事先通知。如果这是一个官方键,请添加文档链接。 - IInspectable

0

IsWindows10OrGreater() 函数来自 VersionHelpers.h

请查看 MSDN 上的版本助手函数注释

文件 VersionHelpers.h 随 Windows 10 SDK 一起提供,但它也可以在之前的版本中使用。只需将其复制到您的开发环境中即可。

这只是一个仅包含头文件定义的小型库,它使用自 Windows 2000 以来在 WinAPI 中可用的 VerSetConditionMaskVerifyVersionInfoW 函数。

更新 如果您无法在源代码中包含清单文件,则可以使用简单的 hack:只需使用 GetFileVersionInfo 函数获取任何系统 dll 的版本,例如 kernel32.dll


3
哦,天啊……“只需将其复制到开发环境中”……你是认真的吗?另外,IsWindows10OrGreater()在Windows 8.1 SDK中声明(这并不意外)。 - IInspectable
@hg_git "许多VS 2015安装默认不包括win10 sdk" - 所有的VS 2015安装默认甚至不包括C++编译器,你如何处理它? - vladon
1
@hg_git 你在寻找本不存在的问题。只需从Windows SDK复制文件或添加安装Windows 10 SDK的要求即可使用你的源代码。就这样。 - vladon
3
函数本身并不能按预期工作。当编写库代码时,您还必须提供清单,但这是无法强制执行的。 - IInspectable
7
你不能“简单复制”一个标题。你有没有考虑可重现的构建环境?如果需要手动设置,那就不是一个解决方案。除非其他人遵循一些自定义说明,否则没有人能够构建。你不能这样做并让它在任何开发者的系统或CI系统上工作。另外,通过使其非标准,你污染了你的构建环境;现在你可能会在代码中引入依赖于你特殊的定制设置的假设。保持简单,不要做出如此鲁莽的更改。 - Roger Leigh
显示剩余15条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接