Windows 8.1预览版发布有一个多月了,相信有不少做Win8 App的朋友想着怎么判断当前系统是8还是8.1。经过几个星期不断的ask、code、search,找到以下四种方法来获取系统版本号,其中两种能够辨别是8还是8.1.
方法一:通过内存寻址,找到Kernel32.dll的内存地址,再找EXE Optional Header 里的版本号
这里用到了PE,关于PE的格式大家可以自行百度。不过大家在使用C++实现的时候可以参考这个svn http://code.google.com/p/arpmrep/source/browse/trunk/ARPackerM_solution/?r=20#ARPackerM_solution%2FHackUtils
有了PE接下来就是内存寻址了
(寻址代码出自Win8开发-千人群:95331609)
1 int getSystemVersion() 2 { 3 LPVOID p = (LPVOID) GetTickCount64; 4 DWORD_PTR addr = (DWORD_PTR) p; 5 addr = addr & 0xffff0000; 6 bool found = false; 7 for(int i = 0;i < 16; i++,addr -= 0x10000) 8 { 9 char *pb = (char*) addr; 10 if(*pb == 'M' && *(pb + 1) == 'Z') 11 { 12 found = true; 13 break; 14 } 15 } 16 if(!found) 17 { 18 return -1; 19 } 20 PE pe(addr); 21 pe.Parse(); 22 const IMAGE_OPTIONAL_HEADER* versionInfo = pe.GetImageOptionalHeader(); 23 return versionInfo->MinorSubsystemVersion; 24 }
此时通过断点监视到如下结果:
由于我用的是Win8所以获取到的最小子版本号为2,如果是Win8.1的话这个值为3。
源码下载(C++)
同时为了方便C++和C#通用,我将内存寻址获取系统子版本号方法新建为一个Windows运行时组件,方便调用。
这里只展示C#调用C++运行时组件的过程:
项目结构:
在GetOSVersion.h中定义一个外部可访问的方法:
1 #pragma once 2 3 namespace GetOSVersionComponent 4 { 5 public ref class GetOSVersion sealed 6 { 7 public: 8 int getSystemVersion(); 9 }; 10 }
在GetOSVersion.cpp中实现如下:
1 #include "pch.h" 2 #include "GetOSVersion.h" 3 #include "minwindef.h" 4 #include "sysinfoapi.h" 5 #include "pe.h" 6 7 using namespace GetOSVersionComponent; 8 using namespace Platform; 9 10 int GetOSVersion::getSystemVersion() 11 { 12 LPVOID p = (LPVOID) GetTickCount64; 13 DWORD_PTR addr = (DWORD_PTR) p; 14 addr = addr & 0xffff0000; 15 bool found = false; 16 for(int i = 0;i < 16; i++,addr -= 0x10000) 17 { 18 char *pb = (char*) addr; 19 if(*pb == 'M' && *(pb + 1) == 'Z') 20 { 21 found = true; 22 break; 23 } 24 } 25 if(!found) 26 { 27 return -1; 28 } 29 PE pe(addr); 30 pe.Parse(); 31 const IMAGE_OPTIONAL_HEADER* versionInfo = pe.GetImageOptionalHeader(); 32 return versionInfo->MinorSubsystemVersion; 33 }
注:不要忘记在C#工程中添加对GetOSVersionComponent工程的引用:
(在Win8 app的后台任务实现的研究中,如果不这样添加的话,会出现即使后台任务的代码再对也会出现应用闪退的情况,MSDN帖子:http://social.msdn.microsoft.com/Forums/zh-CN/a5ee1ae8-9928-4cf3-9694-c9bb0c0106d2/trigger#a5ee1ae8-9928-4cf3-9694-c9bb0c0106d2)
此时,在C#工程的MainPage.xmal.cs中添加引用using GetOSVersionComponent,并写出调用的方法
代码如下:
1 public sealed partial class MainPage : Page 2 { 3 private GetOSVersion getVersion = new GetOSVersion(); 4 5 public MainPage() 6 { 7 this.InitializeComponent(); 8 } 9 10 /// <summary> 11 /// 在此页将要在 Frame 中显示时进行调用。 12 /// </summary> 13 /// <param name="e">描述如何访问此页的事件数据。Parameter 14 /// 属性通常用于配置页。</param> 15 protected override void OnNavigatedTo(NavigationEventArgs e) 16 { 17 int version = getVersion.getSystemVersion(); 18 } 19 }
通过断点可以监视到此时的version值为2,8.1的就是3了
截图如下:
源码下载(C#调用C++运行时组件)
(这个方法非常感谢raptor的指导)
方法二:读取系统的驱动信息
1 /// <summary> 2 /// 可用于自身或导航至 Frame 内部的空白页。 3 /// </summary> 4 public sealed partial class MainPage : Page 5 { 6 public MainPage() 7 { 8 this.InitializeComponent(); 9 } 10 11 /// <summary> 12 /// 在此页将要在 Frame 中显示时进行调用。 13 /// </summary> 14 /// <param name="e">描述如何访问此页的事件数据。Parameter 15 /// 属性通常用于配置页。</param> 16 protected async override void OnNavigatedTo(NavigationEventArgs e) 17 { 18 string version = await GetWindowsVersionAsync(); 19 } 20 21 const string DeviceClassKey = "{A45C254E-DF1C-4EFD-8020-67D146A850E0},10"; 22 const string DeviceDriverVersionKey = "{A8B865DD-2E3D-4094-AD97-E593A70C75D6},3"; 23 const string RootContainer = "{00000000-0000-0000-FFFF-FFFFFFFFFFFF}"; 24 const string RootQuery = "System.Devices.ContainerId:=\"" + RootContainer + "\""; 25 const string HalDeviceClass = "4d36e966-e325-11ce-bfc1-08002be10318"; 26 27 public static async Task<string> GetWindowsVersionAsync() 28 { 29 var hal = await GetHalDevice(DeviceDriverVersionKey); 30 if (hal == null || !hal.Properties.ContainsKey(DeviceDriverVersionKey)) 31 return null; 32 33 var versionParts = hal.Properties[DeviceDriverVersionKey].ToString().Split('.'); 34 return string.Join(".", versionParts.Take(2).ToArray()); 35 } 36 37 private static async Task<PnpObject> GetHalDevice(params string[] properties) 38 { 39 var actualProperties = properties.Concat(new[] { DeviceClassKey }); 40 var rootDevices = await PnpObject.FindAllAsync(PnpObjectType.Device, 41 actualProperties, RootQuery); 42 43 foreach (var rootDevice in rootDevices.Where(d => d.Properties != null && d.Properties.Any())) 44 { 45 var lastProperty = rootDevice.Properties.Last(); 46 if (lastProperty.Value != null) 47 if (lastProperty.Value.ToString().Equals(HalDeviceClass)) 48 return rootDevice; 49 } 50 return null; 51 } 52 }
参考网址:http://attackpattern.com/2013/03/device-information-in-windows-8-store-apps/
Win8的值为6.2,Win8.1的值为6.3。
源码下载(通过驱动获取版本号)
(这个方法非常感谢webabcd的指导)
============================================================
(注:下面两个方法能够获取系统版本号,但不能辨别是8还是8.1)
方法三:通过WebView的userAgent
代码如下:
1 /// <summary> 2 /// 可用于自身或导航至 Frame 内部的空白页。 3 /// </summary> 4 public sealed partial class MainPage : Page 5 { 6 public MainPage() 7 { 8 this.InitializeComponent(); 9 } 10 11 /// <summary> 12 /// 在此页将要在 Frame 中显示时进行调用。 13 /// </summary> 14 /// <param name="e">描述如何访问此页的事件数据。Parameter 15 /// 属性通常用于配置页。</param> 16 protected async override void OnNavigatedTo(NavigationEventArgs e) 17 { 18 string versionInfo = await getUserAgent(); 19 20 var msg = new MessageDialog(versionInfo, "系统信息"); 21 await msg.ShowAsync(); 22 } 23 24 private static Task<string> getUserAgent() 25 { 26 var tcs = new TaskCompletionSource<string>(); 27 WebView webView = new WebView(); 28 29 string htmlFragment = @"<html> 30 <head> 31 <script type='text/javascript'> 32 function GetUserAgent() 33 { 34 return navigator.userAgent; 35 } 36 </script> 37 </head> 38 </html>"; 39 40 webView.LoadCompleted += (sender, e) => 41 { 42 try 43 { 44 string result = webView.InvokeScript("GetUserAgent", null); 45 tcs.TrySetResult(result); 46 } 47 catch(Exception ex) 48 { 49 tcs.TrySetException(ex); 50 } 51 }; 52 53 webView.NavigateToString(htmlFragment); 54 return tcs.Task; 55 } 56 57 public static async Task<string> getOsVersionAsync() 58 { 59 string userAgent = await getUserAgent(); 60 61 //string result = string.Empty; 62 63 //int startIndex = userAgent.ToLower().IndexOf("webview"); 64 //if (startIndex > 0) 65 //{ 66 // int endIndex = userAgent.IndexOf(")", startIndex); 67 68 // if (endIndex > startIndex) 69 // result = userAgent.Substring(startIndex, endIndex - startIndex); 70 //} 71 return userAgent; 72 } 73 }
参考网址:http://www.michielpost.nl/PostDetail_74.aspx
通过运行得到的userAgnet值是一样的(左侧平板模拟器,右侧Win8.1虚拟机):
我们可以看到,试图获取Windosw NT的版本来分辨是8还是8.1行不通。但记住一点,这种方法可以获取系统版本号。
源码下载(userAgent获取系统版本号)
方法四:通过kernel32.dll EntryPoint为GetVersionEx 来获取版本信息
这种方法在Windows商店审核不通过,所以代码和方法我就不贴了,大家去这篇MSDN帖子看吧http://social.msdn.microsoft.com/Forums/zh-CN/3a38fddd-e09e-41b1-b024-87221fffe195/win8881
源码下载(调用系统API获取系统版本号)
第一次写博客,诸多问题研究的不够透彻,还请大家多多指点~~