以前,想要获取 Windows 的版本号很简单,有个 Win32 API 函数名字叫做 GetVersion,望文生义,接下来要做的事情就是去 MSDN 上查下用法就可以了。
现在,GetVersion 会被报告成「过期函数」了。也许还能用,但(据 MSDN 说)起码在 Win10 上是别指望得到预期的结果了。道理也很简单,Win10 都搞滚动升级了,版本号规则肯定也和之前不一样了,你还指望这么老的函数能兼容么?
别痴心妄想了,GetVersionEx 也一样过期。那么,眼下有什么好办法吗?
一般来说,拿 Windows 版本号可能有两种用途:
- 我想看看你 Windows 版本达到我要求没。
- 我就是想知道你 Windows 版本号是多少。
对于前者,微软现在在 MSDN 上是这样推荐的:它做了一组 Version Helper functions,你如果想知道当前 Windows 的版本是不是某个特定的发行版,调这组函数就可以。我们来看看这组函数中三个典型:
- IsWindowsXPOrGreater
- IsWindowsXPSP3OrGreater
- IsWindowsServer
不需要更多说明,我们从名字中就可以看出,这组函数可以用于判断 Windows 的大版本,Service Pack 的版本(结合大版本),以及能知道是不是服务器版操作系统。通常情况下,这些函数大概是够了。
但是,有的时候我们并不关心版本号高低,我们只是想要一个版本号(例如记录日志时)而已。微软对此的建议是:用 GetFileVersionInfo 去获取一个系统 DLL(例如 Kernel32.dll)的文件版本号(原文看 这里)。
相关的代码虽然能找到,MSDN 上也有官方例子(有点小 Bug),但比起一行 GetVersion 来代码量实在是不能算很少。由此可见,处理「过期函数」真的没有想象中那么容易。最后我还是提供一下我从项目代码中挖出来的一个实现吧。别照抄,如果你不想引入 STL 的话:
#include <windows.h>
#include <Strsafe.h>
#pragma comment(lib, "Version.lib")
// 获取文件版本
std::wstring GetFileVersionString(const std::wstring& strFilePath, bool bStrVer = false) {
DWORD dwVerInfoSize = GetFileVersionInfoSize(strFilePath.c_str(), nullptr);
if (dwVerInfoSize) {
std::vector<BYTE> vecVerData(dwVerInfoSize);
if (GetFileVersionInfo(strFilePath.c_str(), NULL, dwVerInfoSize, &vecVerData[0])) {
LPCVOID pBlock = &vecVerData[0];
UINT cbTranslate;
TCHAR SubBlock[MAX_PATH];
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *lpTranslate;
// 阅读语言和代码页列表
VerQueryValue(pBlock,
L"\\VarFileInfo\\Translation",
(LPVOID*)&lpTranslate,
&cbTranslate);
if (bStrVer && lpTranslate) {
// 读取第一种语言和代码页的文件版本
for (size_t i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); ++i) {
StringCchPrintf(SubBlock, sizeof(SubBlock) / sizeof(TCHAR),
L"\\StringFileInfo\\%04x%04x\\ProductVersion",
lpTranslate[i].wLanguage,
lpTranslate[i].wCodePage);
LPVOID lpBuffer = nullptr;
UINT dwBytes;
if (VerQueryValue(pBlock, SubBlock, &lpBuffer, &dwBytes) && lpBuffer && dwBytes > 0) {
std::wstring strVersion(reinterpret_cast<TCHAR*>(lpBuffer));
return strVersion;
}
}
}
// 未找到任何字符串版本
VS_FIXEDFILEINFO* lpffi = nullptr;
UINT uLen = 0;
// 注意:这里的第二个参数 "\" 是固定写法,表示查询根块
if (VerQueryValue(pBlock, L"\\", (LPVOID*)&lpffi, &uLen) && lpffi && uLen >= sizeof(VS_FIXEDFILEINFO)) {
std::wstringstream wos;
wos << HIWORD(lpffi->dwFileVersionMS) << L"." << LOWORD(lpffi->dwFileVersionMS) << L"."
<< HIWORD(lpffi->dwFileVersionLS) << L"." << LOWORD(lpffi->dwFileVersionLS);
return wos.str();
}
}
}
return L"";
}
// 获取 OS 版本信息
std::wstring GetOSVersion(const std::wstring& strWinSysDir, bool bStrVer) {
std::wstring strWinSysFilePath = strWinSysDir;
if (!strWinSysFilePath.empty() && strWinSysFilePath.back() != L'\\') {
strWinSysFilePath += L'\\';
}
return GetFileVersionString(strWinSysFilePath + L"Kernel32.dll", bStrVer);
}
最近迷之高产。
回复删除每天中午除了进餐剩下来的时间从加班改为写Blog,并且前段时间的加班积累了一些素材。
删除