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);
。哇,这是很棒的 API 设计。
它们只是方便的功能。有一个通用的 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
函数,因此IsWindows8Point1OrGreater
和IsWindows10OrGreater
的行为同样受到清单的存在和内容的影响。要为 Windows 8.1 或 Windows 10 显示您的应用程序,请参阅 Targeting your application for Windows。
为了获得 true 操作系统版本(无论表现形式如何),微软建议查询系统 DLL 的文件版本:
Getting the System Version
要获取操作系统的完整版本号,请在其中一个系统DLL上调用
GetFileVersionInfo
函数,例如Kernel32.dll
,然后调用VerQueryValue
获取文件版本信息的\\StringFileInfo\\<lang><codepage>\\ProductVersion
子块.
另一种方法是改用RtlGetVersion()
、NetServerGetInfo()
或NetWkstaGetInfo()
。它们都报告了准确的操作系统版本,并且不受表现形式的影响(还没有?)。
【讨论】:
我不明白“VerQueryValue 获取文件版本信息的\\StringFileInfo\\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 #includeDWORD
构造浮点值永远不会产生小数。如果是这样,就没有办法准确地表示 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++):如何检测罪魁祸首?