C++ 如何检测 Windows 10

Posted

技术标签:

【中文标题】C++ 如何检测 Windows 10【英文标题】:C++ How to detect 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# 的。 【参考方案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://***.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();

【讨论】:

以上是关于C++ 如何检测 Windows 10的主要内容,如果未能解决你的问题,请参考以下文章

如何让 Windows 10 Game Bar 出现在游戏中?

堆栈缓冲区溢出(Windows,C++):如何检测罪魁祸首?

检测 32 位或 64 位 Windows

从 Windows 上的 RSSI 检测蓝牙信号强度

如何训练用于对象检测的潜在 SVM 模型?它使用 Matlab 2012b、Windows 64 位操作系统崩溃

如何检测 C++ Builder 10.4 / Android 应用程序中的泄漏