【问题标题】:C++ How to detect Windows 10C++ 如何检测 Windows 10
【发布时间】:2015-11-13 22:03:28
【问题描述】:

我多年前编写了一个 PC 审计工具,并且一直在更新它。基本功能之一是报告正在审核的 PC 上运行的 Windows 版本,我一直使用 GetVersionEx 调用。

这适用于 Windows 8(包括 Windows 8),但在 Windows 10 下不受支持,实际上 Windows 10 与 Windows 8 一样返回 8.2。 Microsoft 似乎没有引入任何东西作为直接替代,而是建议您检查所需的特定功能而不是查看操作系统,但出于审核目的,我实际上想要操作系统名称。

“扫描仪”是一个必须在非特权帐户下运行的 C++ 程序,所以我认为我没有阅读过其他建议 - 选择系统 DLL 的版本(例如 kernel32.dll)将像这些文件夹一样工作通常用户无法访问。

欢迎任何其他建议/想法!

【问题讨论】:

  • 对于审计工具,您不应该依赖于检测特定版本(10?10 的哪个版本?),而是使用操作系统如何描述自己。这样未来的版本就不会破坏代码。
  • 此外,非特权帐户肯定可以读取系统 DLL,例如 kernel32.dll
  • 您没有检测到 Windows 10! Windows 10 detects you!
  • 投票重新开放,因为目标不同(编程语言也是如此)。检测 Windows 10 与获取 Windows 版本号不同(轻微震惊)。 (诚​​然,一些障碍是相同的,或者至少是相关的。)另外,另一个问题是针对 C# 的。

标签: c++ windows windows-10


【解决方案1】:

GetVersion 和 GetVersionEx 已被 various version helper functions 取代。你想要的是IsWindows10OrGreater。它们可以在 VersionHelpers.h 中找到。

IsWindows10OrGreater 仅在最新的 SDK/Visual Studio 2015 中可用。但是,您可以在一般情况下使用 IsWindowsVersionOrGreater。例如,在我的 7 盒子上,IsWindowsVersionOrGreater(6, 0, 0) 得到 TRUE。

请记住,此函数采用的参数与 Windows 内部版本号相关,而不是营销名称。所以 Windows 8 是 build 6.2。 Windows 7 是 6.0 等。

【讨论】:

  • BOOL WINAPI IsWindows10OrGreater(void);。哇,这是很棒的 AP​​I 设计。
  • 它们只是方便的功能。有一个通用的 IsWindowsVersionOrGreater 函数。
  • 问题是它们在扫描仪必须运行的早期版本的 Windows 中不可用。如果可用,我可以动态加载它们吗?
  • 似乎 ISWindows10OrGreater(或更通用的变体 IsWindowsVersionOrGreater)无法可靠地指示操作系统。现在的行为取决于应用程序“显示”的方式,这意味着即使在 Windows 10 上,应用程序也可以告诉你它在 Windows 8.1 或 Windows 10 上。如果你没有将应用程序定位到 Windows 10,它总是会告诉你视窗 8.1
  • IsWindowsXXOrGreater() 函数根本不是实际函数,它们只是 VerifyVersionInfo() 函数的宏包装器,现在从 Windows 10 开始体现。
【解决方案2】:

我需要它来处理旧版本的 VS 编译器,以及更多在 Qt 框架中的工作。以下是我如何做到这一点的。

将此文件GetWinVersion.h 添加到您的Qt 项目中:

#ifndef GETWINVERSION
#define GETWINVERSION

#include <QtGlobal>

#ifdef Q_OS_WIN

#include <windows.h>
#include <stdio.h>

float GetWinVersion()
{
    OSVERSIONINFO osvi;
    ZeroMemory( &osvi, sizeof(OSVERSIONINFO) );
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    return GetVersionEx( &osvi ) ?
           (float)osvi.dwMajorVersion +
           ((float)osvi.dwMinorVersion/10) :
           0.0 ;
}

#endif //Q_OS_WIN

#endif // GETWINVERSION

在您的 pro 或 pri qmake 文件中添加所需的链接:

msvc: LIBS += -lKernel32

像这样实现辅助函数(注意这里使用的 SystemInfo 是我的一个自定义类,但你明白了......):

#include "GetWinVersion.h"

SystemInfo info;

#ifdef Q_OS_WIN
    info.setPlatform( SystemInfo::WINDOWS );
    switch(QSysInfo::windowsVersion())
    {
    case QSysInfo::WV_32s:        info.setOsName( L"3.1" );     info.setOsVersion( 3.1 ); break;
    case QSysInfo::WV_95:         info.setOsName( L"95" );      info.setOsVersion( 4.0 ); break;
    case QSysInfo::WV_98:         info.setOsName( L"98" );      info.setOsVersion( 4.1 ); break;
    case QSysInfo::WV_Me:         info.setOsName( L"Me" );      info.setOsVersion( 4.9 ); break;
    case QSysInfo::WV_NT:         info.setOsName( L"NT" );      info.setOsVersion( 4.0 ); break;
    case QSysInfo::WV_2000:       info.setOsName( L"2000" );    info.setOsVersion( 5.0 ); break;
    case QSysInfo::WV_XP:         info.setOsName( L"XP" );      info.setOsVersion( 5.1 ); break;
    case QSysInfo::WV_2003:       info.setOsName( L"2003" );    info.setOsVersion( 5.2 ); break;  // Windows Server 2003, Windows Server 2003 R2, Windows Home Server, Windows XP Professional x64 Edition
    case QSysInfo::WV_VISTA:      info.setOsName( L"Vista" );   info.setOsVersion( 6.0 ); break;  // Windows Vista, Windows Server 2008
    case QSysInfo::WV_WINDOWS7:   info.setOsName( L"7" );       info.setOsVersion( 6.1 ); break;  // Windows 7, Windows Server 2008 R2
    case QSysInfo::WV_WINDOWS8:   info.setOsName( L"8" );       info.setOsVersion( 6.2 ); break;  // Windows 8, Windows Server 2012
  // These cases are never reached due to Windows api changes
  // As of Qt 5.5, this not accounted for by QSysInfo::windowsVersion()
  //case QSysInfo::WV_WINDOWS8_1: info.setOsName( L"8.1" );     info.setOsVersion( 6.3 ); break;  // Windows 8.1, Windows Server 2012 R2
  //case QSysInfo::WV_WINDOWS10:  info.setOsName( L"10" );      info.setOsVersion( 10.0 ); break; // Windows 10, Windows Server 2016
    default:
        // On Windows 8.1 & 10, this will only work when the exe
        // contains a manifest which targets the specific OS's
        // you wish to detect.  Else 6.2 (ie. Win 8.0 is returned)
        info.setOsVersion( GetWinVersion() );
        if(      info.osVersion() == 6.3f )  // Windows 8.1, Windows Server 2012 R2
            info.setOsName( L"8.1" );
        else if( info.osVersion() == 10.0f ) // Windows 10, Windows Server 2016
            info.setOsName( L"10" );
        else
            info.setOsName( L"UNKNOWN" );
    }
    info.setOsBits( IsWow64() ? 64 : 32 );
#else
...

现在这是真正的关键。您需要将清单文件附加到您的 exe,该文件将“针对”最近的 Windows 版本,否则您无法检测到它们(请参阅 MS 文档:https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451%28v=vs.85%29.aspx)。这是执行此操作的示例清单:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity 
        name="MyOrg.MyDept.MyAppName" 
        version="1.0.0.0" 
        processorArchitecture="x86" 
        type="win32" />
    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> 
        <application> 
            <!-- Windows 10 --> 
            <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
            <!-- Windows 8.1 -->
            <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
            <!-- Windows 8 -->
            <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
            <!-- Windows 7 -->
            <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>      
            <!-- Windows Vista -->
            <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>          
        </application> 
    </compatibility>
</assembly>

这里有一些附加清单:

set exeFile=MyApp.exe
set manifestFile=MyApp.manifest
set manifestExe=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\x64\mt.exe

"%manifestExe%" -manifest "%manifestFile%" -outputresource:"%exeFile%"

理论上,您可以使用 qmake 运行附加清单的最后一点。我没有找到我找到的例子的运气,现在只是用这批“欺骗”......

【讨论】:

    【解决方案3】:

    从 Windows 8.1 开始,GetVersion()GetVersionEx() 以应用程序表现为准:

    随着 Windows 8.1 的发布,GetVersionEx API 的行为发生了变化,它将为操作系统版本返回的值。 GetVersionEx 函数返回的值现在取决于应用程序的显示方式。

    未针对 Windows 8.1 或 Windows 10 显示的应用程序将返回 Windows 8 操作系统版本值 (6.2)。一旦针对给定的操作系统版本显示应用程序,GetVersionEx 将始终返回应用程序在未来版本中显示的版本。要为 Windows 8.1 或 Windows 10 显示您的应用程序,请参阅Targeting your application for Windows

    较新的Version Helper functions 只是VerifyVersionInfo() 的包装器。从 Windows 10 开始,它现在也受到表现形式的影响:

    Windows 10:如果 lpVersionInfo 参数设置为指定 Windows 8.1 或 Windows 10,则 VerifyVersionInfo 在由不具有 Windows 8.1 或 Windows 10 兼容性清单的应用程序调用时返回 false,即使当前操作系统版本为 Windows 8.1 或 Windows 10。具体而言,VerifyVersionInfo 具有以下行为:

    • 如果应用程序没有清单,VerifyVersionInfo 的行为就像操作系统版本是 Windows 8 (6.2)。
    • 如果应用程序的清单包含与 Windows 8.1 对应的 GUID,VerifyVersionInfo 的行为就像操作系统版本是 Windows 8.1 (6.3)。
    • 如果应用程序的清单包含与 Windows 10 对应的 GUID,VerifyVersionInfo 的行为就像操作系统版本是 Windows 10 (10.0)。

    Version Helper functions 使用VerifyVersionInfo 函数,因此IsWindows8Point1OrGreaterIsWindows10OrGreater 的行为同样受到清单的存在和内容的影响。

    要为 Windows 8.1 或 Windows 10 显示您的应用程序,请参阅 Targeting your application for Windows

    为了获得 true 操作系统版本(无论表现形式如何),微软建议查询系统 DLL 的文件版本:

    Getting the System Version

    要获取操作系统的完整版本号,请在其中一个系统DLL上调用GetFileVersionInfo函数,例如Kernel32.dll,然后调用VerQueryValue获取文件版本信息的\\StringFileInfo\\&lt;lang&gt;&lt;codepage&gt;\\ProductVersion子块.

    另一种方法是改用RtlGetVersion()NetServerGetInfo()NetWkstaGetInfo()。它们都报告了准确的操作系统版本,并且不受表现形式的影响(还没有?)。

    【讨论】:

    • 我不明白“VerQueryValue 获取文件版本信息的\\StringFileInfo\\\\ProductVersion 子块。”你的回答。 "\\StringFileInfo\\\\ProductVersion" 代表什么?文件版本信息的子块是什么意思?
    • @SahilSingh,VerQueryValue() 文档中涵盖了该信息,并且在线有大量示例/教程展示了如何使用 VerQueryValue()。请在提问之前花点时间做一些研究。
    【解决方案4】:

    使用以下函数:

    double getSysOpType()
    {
        double ret = 0.0;
        NTSTATUS(WINAPI *RtlGetVersion)(LPOSVERSIONINFOEXW);
        OSVERSIONINFOEXW osInfo;
    
        *(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion");
    
        if (NULL != RtlGetVersion)
        {
            osInfo.dwOSVersionInfoSize = sizeof(osInfo);
            RtlGetVersion(&osInfo);
            ret = (double)osInfo.dwMajorVersion;
        }
        return ret;
    }
    

    它将以双精度(7、8、8.1、10)返回 Windows 版本。

    【讨论】:

    • 这似乎是最好的解决方案,值得更多的支持:)
    • 谢谢!我们多年来一直在使用这种方法,而且我确实工作得很好!
    • @Bonfire #include
    • 这对我不起作用...它会抛出诸如“RtlGetVersion' is not declared in this scope”之类的错误
    • “它将以双精度 (7, 8, 8.1, 10) 返回 Windows 版本。” - 这是不正确的。从DWORD 构造浮点值永远不会产生小数。如果是这样,就没有办法准确地表示 8.1。虽然这一切都没有实际意义,因为这个函数无论如何都不会返回 8。对于 Windows 10,它返回 10,6 for all versions of Windows from Vista up to 8.1
    【解决方案5】:

    2021-01-12 https://stackoverflow.com/a/52122386/1923561 根据 Michael Haephrati 的回答,我对代码进行了调整。

    enum WindowsOS{
       NotFind,
       Win2000,
       WinXP,
       WinVista,
       Win7,
       Win8,
       Win10
    };
    
    WindowsOS GetOsVersionQuick()
    {
       using namespace std;
       double ret = 0.0;
       NTSTATUS(WINAPI *RtlGetVersion)(LPOSVERSIONINFOEXW);
       OSVERSIONINFOEXW osInfo;
    
       *(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandleA("ntdll"), 
       "RtlGetVersion");
    
       if (NULL != RtlGetVersion)
       {
          osInfo.dwOSVersionInfoSize = sizeof(osInfo);
          RtlGetVersion(&osInfo);
          ret = (double)osInfo.dwMajorVersion;
       }
    
       if (osInfo.dwMajorVersion == 10 && osInfo.dwMinorVersion == 0)
       {
          cout << "this is windows 10\n";
          return Win10;
       }
       else if (osInfo.dwMajorVersion == 6 && osInfo.dwMinorVersion == 3)
       {
          cout << "this is windows 8.1\n";
          return Win8;
       }
       else if (osInfo.dwMajorVersion == 6 && osInfo.dwMinorVersion == 2)
       {
          cout << "this is windows 8\n";
          return Win8;
       }
       else if (osInfo.dwMajorVersion == 6 && osInfo.dwMinorVersion == 1)
       {
          cout << "this is windows 7 or Windows Server 2008 R2\n";
          return Win7;
       }
    
       return NotFind;
    }
    

    2020-06-14

    #include <iostream>
    #include <windows.h>
    #pragma comment(lib, "Version.lib" )
    
    BOOL GetOsVersion()
    {
        wchar_t path[200] = L"C:\\Windows\\System32\\kernel32.dll";
        DWORD dwDummy;
        DWORD dwFVISize = GetFileVersionInfoSize(path, &dwDummy);
        LPBYTE lpVersionInfo = new BYTE[dwFVISize];
        if (GetFileVersionInfo(path, 0, dwFVISize, lpVersionInfo) == 0)
        {
            return FALSE;
        }
    
        UINT uLen;
        VS_FIXEDFILEINFO* lpFfi;
        BOOL bVer = VerQueryValue(lpVersionInfo, L"\\", (LPVOID*)&lpFfi, &uLen);
    
        if (!bVer || uLen == 0)
        {
            return FALSE;
        }
        DWORD dwProductVersionMS = lpFfi->dwProductVersionMS;
        if (HIWORD(dwProductVersionMS) == 10 && LOWORD(dwProductVersionMS) == 0)
        {
            cout << "this is windows 10\n";
        }
        else if (HIWORD(dwProductVersionMS) == 6 && LOWORD(dwProductVersionMS) == 3)
        {
            cout << "this is windows 8.1\n";
        }
        else if (HIWORD(dwProductVersionMS) == 6 && LOWORD(dwProductVersionMS) == 2)
        {
            cout << "this is windows 8\n";
        }
        else if (HIWORD(dwProductVersionMS) == 6 && LOWORD(dwProductVersionMS) == 1)
        {
            cout << "this is windows 7 or Windows Server 2008 R2\n";
        }
        else if (HIWORD(dwProductVersionMS) == 6 && LOWORD(dwProductVersionMS) == 0)
        {
            cout << "this is windows Vista or Windows Server 2008\n";
        }
        else if (HIWORD(dwProductVersionMS) == 5 && LOWORD(dwProductVersionMS) == 2)
        {
            cout << "this is windows Server 2003\n";
        }
        else if (HIWORD(dwProductVersionMS) == 5 && LOWORD(dwProductVersionMS) == 1)
        {
            cout << "this is windows Server XP\n";
        }
        else if (HIWORD(dwProductVersionMS) == 5 && LOWORD(dwProductVersionMS) == 0)
        {
            cout << "this is windows 2000\n";
        }
        //else if (lpFfi->dwFileVersionMS == 4 && lpFfi->dwFileVersionLS == 90)
        //{
        //    cout << "this is windows  Me\n";
        //}
        //else if (lpFfi->dwFileVersionMS == 4 && lpFfi->dwFileVersionLS == 10)
        //{
        //    cout << "this is windows  98\n";
        //}
        //else if (lpFfi->dwFileVersionMS == 4 && lpFfi->dwFileVersionLS == 0)
        //{
        //    cout << "this is windows  95\n";
        //}
        return TRUE;
    }
    

    经过测试用于检测win10的代码。

    我推测这个 api 错误 IsWindows10OrGreater 是因为为 kernel32.dll 设置了错误的 FileVersionMS 版本。使用ProductVersionMS版本查询正常获取。

    https://docs.microsoft.com/en-us/windows/win32/api/versionhelpers/nf-versionhelpers-iswindows10orgreater

    希望对大家有所帮助!

    【讨论】:

      【解决方案6】:

      不要使用VersionHelpers.h!有问题!

      它忽略用户的应用程序兼容性设置。

      改为使用较旧的 Kernel32.dll 函数,例如 GetVersion,例如:

      bool IsWindowsVersionOrGreater(unsigned short version)
      {
          return _byteswap_ushort((unsigned short)GetVersion()) >= version;
      }
      
      // Usage: IsWindowsVersionOrGreater(_WIN32_WINNT_WINTHRESHOLD)
      

      【讨论】:

        【解决方案7】:

        FWIW,LibreOffice 项目通过getOSVersion() 提供版本字符串

        OUString WinSalInstance::getOSVersion()
        {
            OUStringBuffer aVer(50); // capacity for string like "Windows 6.1 Service Pack 1 build 7601"
            aVer.append("Windows ");
            // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
            // subject to manifest-based behavior since Windows 8.1, so give wrong results.
            // Another approach would be to use NetWkstaGetInfo, but that has some small
            // reported delays (some milliseconds), and might get slower in domains with
            // poor network connections.
            // So go with a solution described at https://web.archive.org/web/20090228100958/http://msdn.microsoft.com/en-us/library/ms724429.aspx
            bool bHaveVerFromKernel32 = false;
            if (HMODULE h_kernel32 = GetModuleHandleW(L"kernel32.dll"))
            {
                wchar_t szPath[MAX_PATH];
                DWORD dwCount = GetModuleFileNameW(h_kernel32, szPath, SAL_N_ELEMENTS(szPath));
                if (dwCount != 0 && dwCount < SAL_N_ELEMENTS(szPath))
                {
                    dwCount = GetFileVersionInfoSizeW(szPath, nullptr);
                    if (dwCount != 0)
                    {
                        std::unique_ptr<char[]> ver(new char[dwCount]);
                        if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
                        {
                            void* pBlock = nullptr;
                            UINT dwBlockSz = 0;
                            if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE && dwBlockSz >= sizeof(VS_FIXEDFILEINFO))
                            {
                                VS_FIXEDFILEINFO* vi1 = static_cast<VS_FIXEDFILEINFO*>(pBlock);
                                aVer.append(OUString::number(HIWORD(vi1->dwProductVersionMS)) + "."
                                            + OUString::number(LOWORD(vi1->dwProductVersionMS)));
                                bHaveVerFromKernel32 = true;
                            }
                        }
                    }
                }
            }
            // Now use RtlGetVersion (which is not subject to deprecation for GetVersion(Ex) API)
            // to get build number and SP info
            bool bHaveVerFromRtlGetVersion = false;
            if (HMODULE h_ntdll = GetModuleHandleW(L"ntdll.dll"))
            {
                if (auto RtlGetVersion
                    = reinterpret_cast<RtlGetVersion_t>(GetProcAddress(h_ntdll, "RtlGetVersion")))
                {
                    RTL_OSVERSIONINFOW vi2{}; // initialize with zeroes - a better alternative to memset
                    vi2.dwOSVersionInfoSize = sizeof(vi2);
                    if (STATUS_SUCCESS == RtlGetVersion(&vi2))
                    {
                        if (!bHaveVerFromKernel32) // we failed above; let's hope this would be useful
                            aVer.append(OUString::number(vi2.dwMajorVersion) + "."
                                        + OUString::number(vi2.dwMinorVersion));
                        aVer.append(" ");
                        if (vi2.szCSDVersion[0])
                            aVer.append(OUString::Concat(o3tl::toU(vi2.szCSDVersion)) + " ");
                        aVer.append("Build " + OUString::number(vi2.dwBuildNumber));
                        bHaveVerFromRtlGetVersion = true;
                    }
                }
            }
            if (!bHaveVerFromKernel32 && !bHaveVerFromRtlGetVersion)
                aVer.append("unknown");
            return aVer.makeStringAndClear();
        }
        

        【讨论】:

          猜你喜欢
          • 2018-05-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多