实战经验:在Windows 8.1/10上获取具体的系统版本号

实战经验:在Windows 8.1/10上获取具体的系统版本号

作者:BlogUpdater |  时间:2019-03-03 |  浏览:2403 |  评论已关闭 条评论

碰到这样一个问题,需要获取当前运行系统的版本号。

对于Windows系统来说,主要需要关注的是两个版本号:主版本,次版本。例如:Windows 7系统的版本号为:6.1。Windows 10的版本号为:10.0。过去我们可以使用Windows API GetVersionEx来获取系统版本,但是从Windows8.1开始,如果程序没有进行Manifest(原谅我也不知道怎么翻译这个词比较合适,所以直接用原文),则这个API将返回Windows 8的版本,也即:当你的程序跑在Windows 10上,而且程序没有Manifest,则GetVersionEx将直接返回Windows 8的版本号(6.2)。
不太明白微软这样设计的原因,但是问题确实是出现了,有时我们并不需要去判断某个特性是否在当前系统上可用,但我们需要准确的知道当前系统的版本。如何解决这个问题呢?

解决方法1:使用未文档化的API
ntdll中有一个未文档化的API,叫做RtlGetNtVersionNumbers。我们可以使用这个函数获取系统的版本号,而不需要显式的Manifest。
先来看示例代码:

typedef void(__stdcall*NTPROC)(DWORD*, DWORD*, DWORD*);
HINSTANCE hinst = LoadLibrary(_T("ntdll.dll"));
DWORD dwBuildNumber = 0;
NTPROC pFunc = (NTPROC)GetProcAddress(hinst, "RtlGetNtVersionNumbers"); 
pFunc(&dwMajor, &dwMinor, &dwBuildNumber); 
if (dwMajor == 6 && dwMinor == 3)
{
	return _T("Windows 8.1");
}
if (dwMajor == 10 && dwMinor == 0)	
{
	return _T("Windows 10");
}

代码解释如下:
1) 因为此API从ntdll.dll中导出,所以我们首先尝试加载ntdll.dll这个文件,加载成功后,使用GetProcAddress来获取函数指针,然后进行调用。
2) 如果版本号为6.3,则表示当前系统是Windows 8。如果是10.0,则表示当前系统是Windows 10。
3) 这里需要注意的是,我没有进行完整的错误处理,代码只是演示了基本思路。
4) 另外,从MSDN中可以知道,当版本是6.3的时候,系统可能是Windows 8,也可能是Windows Server 2012 R2,所以,还需要对此种情况做进一步判断。

解决方法2:添加Manifest
这种方法就是微软所推荐的方法了。具体步骤如下:
1) 创建一个manifest文件,例如:test.manifest。
2) 将如下内容填充到test.manifest中。

<?xml version="1.0" encoding="UTF-8"?>   
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <!-- For GetVersionEx on Windows 8.1/10 -->  
    <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 Vista -->  
            <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>   
            <!-- Windows 7 -->  
            <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>  
            <!-- Windows 8 -->  
            <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>  
        </application>   
    </compatibility>  
</assembly>

3) 在VS中,在【项目-属性-清单工具-输入输出-额外的清单文件】中指定这个test.manifest文件。
4) 重新编译。

经过以上步骤之后,我们就可以在Windows 8.1/10上准确的获取系统版本号了。添加了这个清单文件,不会影响程序在Windows 7/8上的正常运行。另外需要注意的是,当以后新的操作系统发布,我们需要根据新系统的ID对test.manifest进行修改。

总结
1) 使用未文档化的方法虽然可以获取版本号,也不需要指定Manifest,但是因为这个API不是SDK的一部分,微软有权在将来的系统中变更甚至取消这个API,所以,尽量不推荐使用此种方法。
2) 在VS2010中,使用清单的方式来获取版本号比较方便有效,但MT.exe工具有Bug,编译时会出现以下警告信息。
warning 81010002: Unrecognized Element “compatibility” in namespace
解决方法:升级VS到最新版本,或者向微软请求一个hotfix来解决此问题。

标签:

评论已关闭。