如何检测真正的Windows版本?

我知道我可以调用GetVersionEx Win32 API函数来检索Windows版本。 在大多数情况下,返回值反映了我的Windows版本,但有时并不是这样。

如果用户在兼容层下运行我的应用程序,则GetVersionEx将不会报告实际版本,而是报告兼容性层执行的版本。 例如,如果我正在运行Vista并以“Windows NT 4”兼容模式执行我的程序,则GetVersionEx将不会返回6.0版本,而是4.0版本。

有没有办法绕过这种行为,并获得真正的Windows版本?

我知道的最好的方法是检查是否从某个DLL导出特定的API。 每个新的Windows版本都增加了新的function,并且通过检查这些function的存在可以说明应用程序在哪个操作系统上运行。 例如,Vista从kernel32.dll中导出GetLocaleInfoEx ,而以前的Windows则没有。

长话短说,这里是一个只包含kernel32.dll导出的列表。

 > *function:在*  
 > GetLocaleInfoEx:Vista  
 > GetLargePageMinimum:Vista,Server 2003  
 GetDLLDirectory:Vista,Server 2003,XP SP1  
 GetNativeSystemInfo:Vista,Server 2003,XP SP1,XP  
 ReplaceFile:Vista,Server 2003,XP SP1,XP,2000  
 OpenThread:Vista,Server 2003,XP SP1,XP,2000,ME  
 GetThreadPriorityBoost:Vista,Server 2003,XP SP1,XP,2000,NT 4  
 IsDebuggerPresent:Vista,Server 2003,XP SP1,XP,2000,ME,NT 4,98   
 GetDiskFreeSpaceEx:Vista,Server 2003,XP SP1,XP,2000,ME,NT 4,98,95 OSR2  
 ConnectNamedPipe:Vista,Server 2003,XP SP1,XP,2000,NT 4,NT 3  
哔声:Vista,Server 2003,XP SP1,XP,2000,ME,98,95 OSR2,95  

编写函数来确定真正的操作系统版本很简单; 只是从最新的操作系统到最早的操作系统,并使用GetProcAddress来检查导出的API。 用任何语言来实现这个应该是微不足道的。

Delphi中的以下代码是从免费的DSiWin32库中提取的):

TDSiWindowsVersion = (wvUnknown, wvWin31, wvWin95, wvWin95OSR2, wvWin98, wvWin98SE, wvWinME, wvWin9x, wvWinNT3, wvWinNT4, wvWin2000, wvWinXP, wvWinNT, wvWinServer2003, wvWinVista); function DSiGetWindowsVersion: TDSiWindowsVersion; var versionInfo: TOSVersionInfo; begin versionInfo.dwOSVersionInfoSize := SizeOf(versionInfo); GetVersionEx(versionInfo); Result := wvUnknown; case versionInfo.dwPlatformID of VER_PLATFORM_WIN32s: Result := wvWin31; VER_PLATFORM_WIN32_WINDOWS: case versionInfo.dwMinorVersion of 0: if Trim(versionInfo.szCSDVersion[1]) = 'B' then Result := wvWin95OSR2 else Result := wvWin95; 10: if Trim(versionInfo.szCSDVersion[1]) = 'A' then Result := wvWin98SE else Result := wvWin98; 90: if (versionInfo.dwBuildNumber = 73010104) then Result := wvWinME; else Result := wvWin9x; end; //case versionInfo.dwMinorVersion VER_PLATFORM_WIN32_NT: case versionInfo.dwMajorVersion of 3: Result := wvWinNT3; 4: Result := wvWinNT4; 5: case versionInfo.dwMinorVersion of 0: Result := wvWin2000; 1: Result := wvWinXP; 2: Result := wvWinServer2003; else Result := wvWinNT end; //case versionInfo.dwMinorVersion 6: Result := wvWinVista; end; //case versionInfo.dwMajorVersion end; //versionInfo.dwPlatformID end; { DSiGetWindowsVersion } function DSiGetTrueWindowsVersion: TDSiWindowsVersion; function ExportsAPI(module: HMODULE; const apiName: string): boolean; begin Result := GetProcAddress(module, PChar(apiName)) <> nil; end; { ExportsAPI } var hKernel32: HMODULE; begin { DSiGetTrueWindowsVersion } hKernel32 := GetModuleHandle('kernel32'); Win32Check(hKernel32 <> 0); if ExportsAPI(hKernel32, 'GetLocaleInfoEx') then Result := wvWinVista else if ExportsAPI(hKernel32, 'GetLargePageMinimum') then Result := wvWinServer2003 else if ExportsAPI(hKernel32, 'GetNativeSystemInfo') then Result := wvWinXP else if ExportsAPI(hKernel32, 'ReplaceFile') then Result := wvWin2000 else if ExportsAPI(hKernel32, 'OpenThread') then Result := wvWinME else if ExportsAPI(hKernel32, 'GetThreadPriorityBoost') then Result := wvWinNT4 else if ExportsAPI(hKernel32, 'IsDebuggerPresent') then //is also in NT4! Result := wvWin98 else if ExportsAPI(hKernel32, 'GetDiskFreeSpaceEx') then //is also in NT4! Result := wvWin95OSR2 else if ExportsAPI(hKernel32, 'ConnectNamedPipe') then Result := wvWinNT3 else if ExportsAPI(hKernel32, 'Beep') then Result := wvWin95 else // we have no idea Result := DSiGetWindowsVersion; end; { DSiGetTrueWindowsVersion } 

—更新2009-10-09

事实certificate,在Vista SP1及更高版本上执行“无证”操作系统检测变得非常困难。 看看API的变化,显示所有的Windows 2008function也在Vista SP1中实现,所有的Windows 7function也在Windows 2008 R2中实现。 太糟糕了 :(

—更新结束

FWIW,这是我在练习中遇到的一个问题。 我们(我工作的公司)有一个程序,当Vista发布时(甚至几个星期之后),这个程序并没有真正实现Vista。 它不在兼容层下工作。 (一些DirectX问题,不要问)

我们并不想为自己的好用户在Vista上运行这个应用程序 – 兼容性或不兼容 – 所以我必须find一个解决scheme(比我更聪明的人指出我正确的方向;上面的东西不是我的心血结晶)。 现在我把它寄给你们,为将来要解决这个问题的所有可怜的人提供帮助。 谷歌,请索引这篇文章!

如果您有更好的解决scheme(或对我的升级和/或修复),请在这里发布答案…

WMI Query:

 "Select * from Win32_OperatingSystem" 

编辑:其实更好的是:

 "Select Version from Win32_OperatingSystem" 

你可以这样在Delphi中实现:

 function OperatingSystemDisplayName: string; function GetWMIObject(const objectName: string): IDispatch; var chEaten: Integer; BindCtx: IBindCtx; Moniker: IMoniker; begin OleCheck(CreateBindCtx(0, bindCtx)); OleCheck(MkParseDisplayName(BindCtx, PChar(objectName), chEaten, Moniker)); OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result)); end; function VarToString(const Value: OleVariant): string; begin if VarIsStr(Value) then begin Result := Trim(Value); end else begin Result := ''; end; end; function FullVersionString(const Item: OleVariant): string; var Caption, ServicePack, Version, Architecture: string; begin Caption := VarToString(Item.Caption); ServicePack := VarToString(Item.CSDVersion); Version := VarToString(Item.Version); Architecture := ArchitectureDisplayName(SystemArchitecture); Result := Caption; if ServicePack <> '' then begin Result := Result + ' ' + ServicePack; end; Result := Result + ', version ' + Version + ', ' + Architecture; end; var objWMIService: OleVariant; colItems: OleVariant; Item: OleVariant; oEnum: IEnumvariant; iValue: LongWord; begin Try objWMIService := GetWMIObject('winmgmts:\\localhost\root\cimv2'); colItems := objWMIService.ExecQuery('SELECT Caption, CSDVersion, Version FROM Win32_OperatingSystem', 'WQL', 0); oEnum := IUnknown(colItems._NewEnum) as IEnumVariant; if oEnum.Next(1, Item, iValue)=0 then begin Result := FullVersionString(Item); exit; end; Except // yes, I know this is nasty, but come what may I want to use the fallback code below should the WMI code fail End; (* Fallback, relies on the deprecated function GetVersionEx, reports erroneous values when manifest does not contain supportedOS matching the executing system *) Result := TOSVersion.ToString; end; 

如何获取系统文件的版本?

最好的文件是kernel32.dll,位于%WINDIR%\ System32 \ kernel32.dll。

有API来获取文件版本。 例如:我正在使用Windows XP – >“5.1.2600.5512(xpsp.080413-2111)”

另一个scheme

阅读以下registry项:

 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductName 

或从其他键

 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion 

真实版本存储在PEB过程信息块上。

Win32应用程序示例(Delphi代码)

 unit RealWindowsVerUnit; interface uses Windows; var //Real version Windows Win32MajorVersionReal: Integer; Win32MinorVersionReal: Integer; implementation type PPEB=^PEB; PEB = record InheritedAddressSpace: Boolean; ReadImageFileExecOptions: Boolean; BeingDebugged: Boolean; Spare: Boolean; Mutant: Cardinal; ImageBaseAddress: Pointer; LoaderData: Pointer; ProcessParameters: Pointer; //PRTL_USER_PROCESS_PARAMETERS; SubSystemData: Pointer; ProcessHeap: Pointer; FastPebLock: Pointer; FastPebLockRoutine: Pointer; FastPebUnlockRoutine: Pointer; EnvironmentUpdateCount: Cardinal; KernelCallbackTable: PPointer; EventLogSection: Pointer; EventLog: Pointer; FreeList: Pointer; //PPEB_FREE_BLOCK; TlsExpansionCounter: Cardinal; TlsBitmap: Pointer; TlsBitmapBits: array[0..1] of Cardinal; ReadOnlySharedMemoryBase: Pointer; ReadOnlySharedMemoryHeap: Pointer; ReadOnlyStaticServerData: PPointer; AnsiCodePageData: Pointer; OemCodePageData: Pointer; UnicodeCaseTableData: Pointer; NumberOfProcessors: Cardinal; NtGlobalFlag: Cardinal; Spare2: array[0..3] of Byte; CriticalSectionTimeout: LARGE_INTEGER; HeapSegmentReserve: Cardinal; HeapSegmentCommit: Cardinal; HeapDeCommitTotalFreeThreshold: Cardinal; HeapDeCommitFreeBlockThreshold: Cardinal; NumberOfHeaps: Cardinal; MaximumNumberOfHeaps: Cardinal; ProcessHeaps: Pointer; GdiSharedHandleTable: Pointer; ProcessStarterHelper: Pointer; GdiDCAttributeList: Pointer; LoaderLock: Pointer; OSMajorVersion: Cardinal; OSMinorVersion: Cardinal; OSBuildNumber: Cardinal; OSPlatformId: Cardinal; ImageSubSystem: Cardinal; ImageSubSystemMajorVersion: Cardinal; ImageSubSystemMinorVersion: Cardinal; GdiHandleBuffer: array [0..33] of Cardinal; PostProcessInitRoutine: Cardinal; TlsExpansionBitmap: Cardinal; TlsExpansionBitmapBits: array [0..127] of Byte; SessionId: Cardinal; end; //Get PEB block current win32 process function GetPDB: PPEB; stdcall; asm MOV EAX, DWORD PTR FS:[30h] end; initialization //Detect true windows wersion Win32MajorVersionReal := GetPDB^.OSMajorVersion; Win32MinorVersionReal := GetPDB^.OSMinorVersion; end. 

下面在Windows 10中适用于没有在应用程序清单中列出的Windows 10 GUID:

 uses System.SysUtils, Winapi.Windows; type NET_API_STATUS = DWORD; _SERVER_INFO_101 = record sv101_platform_id: DWORD; sv101_name: LPWSTR; sv101_version_major: DWORD; sv101_version_minor: DWORD; sv101_type: DWORD; sv101_comment: LPWSTR; end; SERVER_INFO_101 = _SERVER_INFO_101; PSERVER_INFO_101 = ^SERVER_INFO_101; LPSERVER_INFO_101 = PSERVER_INFO_101; const MAJOR_VERSION_MASK = $0F; function NetServerGetInfo(servername: LPWSTR; level: DWORD; var bufptr): NET_API_STATUS; stdcall; external 'Netapi32.dll'; function NetApiBufferFree(Buffer: LPVOID): NET_API_STATUS; stdcall; external 'Netapi32.dll'; type pfnRtlGetVersion = function(var RTL_OSVERSIONINFOEXW): LONG; stdcall; var Buffer: PSERVER_INFO_101; ver: RTL_OSVERSIONINFOEXW; RtlGetVersion: pfnRtlGetVersion; begin Buffer := nil; // Win32MajorVersion and Win32MinorVersion are populated from GetVersionEx()... ShowMessage(Format('GetVersionEx: %d.%d', [Win32MajorVersion, Win32MinorVersion])); // shows 6.2, as expected per GetVersionEx() documentation @RtlGetVersion := GetProcAddress(GetModuleHandle('ntdll.dll'), 'RtlGetVersion'); if Assigned(RtlGetVersion) then begin ZeroMemory(@ver, SizeOf(ver)); ver.dwOSVersionInfoSize := SizeOf(ver); if RtlGetVersion(ver) = 0 then ShowMessage(Format('RtlGetVersion: %d.%d', [ver.dwMajorVersion, ver.dwMinorVersion])); // shows 10.0 end; if NetServerGetInfo(nil, 101, Buffer) = NO_ERROR then try ShowMessage(Format('NetServerGetInfo: %d.%d', [Buffer.sv101_version_major and MAJOR_VERSION_MASK, Buffer.sv101_version_minor])); // shows 10.0 finally NetApiBufferFree(Buffer); end; end. 

更新NetWkstaGetInfo()也可能工作,类似于'NetServerGetInfo()`,但我还没有尝试过呢。

本质上是为了回答重复问题: 为Windows 8.1和Delphi 2007开发操作系统主要版本,次要版本和生成版本

从W2K开始,您可以使用NetServerGetInfo 。 NetServerGetInfo在W7和W8.1上返回正确的信息,无法在W10上testing。

 function GetWinVersion: string; var Buffer: PServerInfo101; begin Buffer := nil; if NetServerGetInfo(nil, 101, Pointer(Buffer)) = NO_ERROR then try Result := <Build You Version String here>( Buffer.sv101_version_major, Buffer.sv101_version_minor, VER_PLATFORM_WIN32_NT // Save since minimum support begins in W2K ); finally NetApiBufferFree(Buffer); end; end; 

关于使用NetServerGetInfo()的一个注意事项,它仍然在Windows 10(10240.th1_st1)上工作…

https://msdn.microsoft.com/en-us/library/windows/desktop/aa370903%28v=vs.85%29.aspx

sv101_version_major

主版本号和服务器types。

操作系统的主要版本号以最低有效4位指定。 服务器types被指定为最重要的4位。 应用程序将使用Lmserver.h头文件{0x0F}中定义的MAJOR_VERSION_MASK位掩码,以从该成员获取主版本号。

换句话说,(sv101_version_major&MAJOR_VERSION_MASK)。

注意: Gabr在询问可以绕过GetVersionEx限制的方法。 JCL代码使用GetVersionEx,因此受到兼容性层的影响。 此信息仅适用于不需要绕过兼容层的人员。

使用Jedi JCL,可以添加单元JclSysInfo,并调用函数GetWindowsVersion 。 它返回一个枚举typesTWindowsVersion。

目前,JCL包含所有发货的Windows版本,每当Microsoft在一个盒子中发布一个新版本的Windows时,它就会被更改:

  TWindowsVersion = (wvUnknown, wvWin95, wvWin95OSR2, wvWin98, wvWin98SE, wvWinME, wvWinNT31, wvWinNT35, wvWinNT351, wvWinNT4, wvWin2000, wvWinXP, wvWin2003, wvWinXP64, wvWin2003R2, wvWinVista, wvWinServer2008, wvWin7, wvWinServer2008R2); 

如果您想知道是否运行64位Windows 7而不是32位,请调用JclSysInfo.IsWindows64

请注意,JCL allso处理版本,如专业版,旗舰版等。对于该调用GetWindowsEdition,它返回其中之一:

 TWindowsEdition = (weUnknown, weWinXPHome, weWinXPPro, weWinXPHomeN, weWinXPProN, weWinXPHomeK, weWinXPProK, weWinXPHomeKN, weWinXPProKN, weWinXPStarter, weWinXPMediaCenter, weWinXPTablet, weWinVistaStarter, weWinVistaHomeBasic, weWinVistaHomeBasicN, weWinVistaHomePremium, weWinVistaBusiness, weWinVistaBusinessN, weWinVistaEnterprise, weWinVistaUltimate, weWin7Starter, weWin7HomeBasic, weWin7HomePremium, weWin7Professional, weWin7Enterprise, weWin7Ultimate); 

出于历史的考虑,您可以使用NtProductType函数检查NT级版本,它将返回:

  TNtProductType =    (ptUnknown, ptWorkStation, ptServer, ptAdvancedServer,   ptPersonal, ptProfessional, ptDatacenterServer, ptEnterprise, ptWebEdition); 

请注意,上面检测到“N版本”。 这是欧盟(欧盟)版本的Windows,由于欧盟反托拉斯法规。 JCL内部的检测层次非常好。

这里有一个示例函数,可以帮助你检测Vista,并在Vista上做一些特殊的事情。

 function IsSupported:Boolean; begin case GetWindowsVersion of wvVista: result := false; else result := true; end; end; 

请注意,如果你想做“大于”检查,那么你应该只使用其他技术。 还要注意,版本检查经常会成为未来破产的根源。 我通常select警告用户并继续,以便我的二进制代码不会成为未来实际的破坏源。

最近我试图安装一个应用程序,安装程序检查我的驱动器的可用空间,并不会安装,因为我有超过2千兆字节的可用空间。 安装程序中的32位整数有符号值变为负值,从而中断了安装程序。 我必须将其安装到虚拟机才能使其工作。 添加“智能代码”往往使您的应用程序“笨蛋”。 警惕。

顺便说一句,我发现从命令行,你可以运行WMIC.exe,并键入path Win32_OperatingSystem (“select*从Win32_OperatingSystem”没有为我工作)。 将来也许JCL可以扩展到使用WMI信息。