¿Cómo detectar la verdadera versión de Windows?

Sé que puedo llamar a la función GetVersionEx Win32 API para recuperar la versión de Windows. En la mayoría de los casos, el valor devuelto refleja la versión de mi Windows, pero a veces eso no es así.

Si un usuario ejecuta mi aplicación debajo de la capa de compatibilidad, GetVersionEx no informará la versión real, sino la versión impuesta por la capa de compatibilidad. Por ejemplo, si estoy ejecutando Vista y ejecutando mi progtwig en el modo de compatibilidad “Windows NT 4”, GetVersionEx no devolverá la versión 6.0 sino la 4.0.

¿Hay alguna forma de eludir este comportamiento y obtener una versión verdadera de Windows?

El mejor enfoque que conozco es verificar si API específica se exporta desde alguna DLL. Cada nueva versión de Windows agrega nuevas funciones y al verificar la existencia de esas funciones, uno puede decir en qué sistema operativo se está ejecutando la aplicación. Por ejemplo, Vista exporta GetLocaleInfoEx desde kernel32.dll, mientras que los Windows anteriores no lo hicieron.

Para abreviar, esta es una lista que solo contiene exportaciones de kernel32.dll.

 > * función: implementada en *  
 > 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  
 Pitido: Vista, Servidor 2003, XP SP1, XP, 2000, ME, 98, 95 OSR2, 95  

Escribir la función para determinar la versión real del sistema operativo es simple; solo proceda desde el sistema operativo más reciente hasta el más antiguo y use GetProcAddress para verificar las API exportadas. Implementar esto en cualquier idioma debe ser trivial.

El siguiente código en Delphi se extrajo de la biblioteca gratuita 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 } 

— actualizado 2009-10-09

Resulta que es muy difícil hacer una detección de sistema operativo “no documentada” en Vista SP1 y superior. Una mirada a los cambios de la API muestra que todas las funciones de Windows 2008 también se implementan en Vista SP1 y que todas las funciones de Windows 7 también se implementan en Windows 2008 R2. Demasiado 🙁

— fin de la actualización

FWIW, este es un problema que encontré en la práctica. Nosotros (la empresa para la que trabajo) tenemos un progtwig que realmente no estaba preparado para Vista cuando se lanzó Vista (y algunas semanas después de eso …). Tampoco funcionaba bajo la capa de compatibilidad. (Algunos problemas de DirectX. No preguntes)

No queríamos que los usuarios demasiado astutos para sí mismos ejecutaran esta aplicación en Vista, modo compatibilidad o no, así que tuve que encontrar una solución (un tipo más listo que yo me indicó la dirección correcta; las cosas de arriba no son mi idea original). Ahora lo estoy publicando para su gusto y para ayudar a todas las almas pobres que tendrán que resolver este problema en el futuro. Google, por favor indexe este artículo!

Si tiene una mejor solución (o una actualización y / o solución para la mía), publique una respuesta aquí …

WMI QUery:

 "Select * from Win32_OperatingSystem" 

EDITAR: En realidad, sería mejor:

 "Select Version from Win32_OperatingSystem" 

Podrías implementar esto en Delphi así:

 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; 

¿Qué hay de obtener la versión de un archivo de sistema?

El mejor archivo sería kernel32.dll, que se encuentra en% WINDIR% \ System32 \ kernel32.dll.

Hay API para obtener la versión del archivo. Ejemplo: estoy usando Windows XP -> “5.1.2600.5512 (xpsp.080413-2111)”

Otra solución:

lea la siguiente entrada de registro:

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

u otras claves de

 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion 

tienda de versión real en bloque PEB de información de proceso.

Ejemplo para la aplicación Win32 (Código 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. 

Lo siguiente funciona para mí en Windows 10 sin el GUID de Windows 10 enumerado en el manifiesto de la aplicación:

 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. 

Actualización : NetWkstaGetInfo() probablemente también funcione, similar a ‘NetServerGetInfo () `, pero aún no lo he intentado.

Nota: Gabr pregunta sobre un enfoque que puede eludir las limitaciones de GetVersionEx . El código JCL usa GetVersionEx y, por lo tanto, está sujeto a la capa de compatibilidad. Esta información es para personas que no necesitan pasar por alto la capa de compatibilidad, solo.

Con Jedi JCL, puede agregar la unidad JclSysInfo y llamar a la función GetWindowsVersion . Devuelve un tipo enumerado TWindowsVersion.

Actualmente JCL contiene todas las versiones de Windows enviadas, y se cambia cada vez que Microsoft envía una nueva versión de Windows en una caja:

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

Si desea saber si está ejecutando Windows 7 de 64 bits en lugar de 32 bits, llame a JclSysInfo.IsWindows64 .

Tenga en cuenta que JCL también maneja Ediciones, como Pro, Ultimate, etc. Para esa llamada GetWindowsEdition, y devuelve uno de estos:

 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); 

Para un interés histórico, también puedes consultar la edición a nivel de NT con la función NtProductType, que devuelve:

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

Tenga en cuenta que “N ediciones” se detectaron anteriormente. Esa es una versión de Windows de la UE (Europa), creada debido a las regulaciones antimonopolio de la UE. Esa es una gradación de detección bastante fina dentro del JCL.

Aquí hay una función de muestra que lo ayudará a detectar Vista y hacer algo especial cuando esté en Vista.

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

Tenga en cuenta que si desea hacer una verificación “mayor que”, entonces debe usar otras técnicas. También tenga en cuenta que la verificación de versiones a menudo puede ser una fuente de ruptura en el futuro. Por lo general, he decidido advertir a los usuarios y continuar, para que mi código binario no se convierta en la fuente real de roturas en el futuro.

Recientemente traté de instalar una aplicación y el instalador comprobó el espacio libre de mi disco y no se instaló porque tenía más de 2 gigabytes de espacio libre. El valor entero de 32 bits firmado en el instalador se volvió negativo, rompiendo el instalador. Tuve que instalarlo en una máquina virtual para que funcione. Agregar “código inteligente” a menudo hace que su aplicación sea “más estúpida”. Sé cauteloso.

Incidentalmente, encontré que desde la línea de comandos, puede ejecutar WMIC.exe y escribir path Win32_OperatingSystem (El “Seleccionar * de Win32_OperatingSystem” no funcionó para mí). En el futuro, tal vez JCL podría extenderse para usar la información de WMI.

Básicamente, para responder el duplicado. Q: obtener versiones de SO mayores, menores y de comstackción para Windows 8.1 y versiones posteriores en Delphi 2007.

Comenzando con W2K puede usar NetServerGetInfo . NetServerGetInfo devuelve la información correcta en W7 y W8.1, no se puede probar en W10 ..

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

Una nota sobre el uso de NetServerGetInfo (), que todavía funciona en Windows 10 (10240.th1_st1) …

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

sv101_version_major

El número de versión principal y el tipo de servidor.

El número de versión de lanzamiento principal del sistema operativo se especifica en los 4 bits menos significativos. El tipo de servidor se especifica en los 4 bits más significativos. La máscara de bits MAJOR_VERSION_MASK definida en el encabezado Lmserver.h {0x0F} debe ser utilizada por una aplicación para obtener el número de versión principal de este miembro.

En otras palabras, (sv101_version_major & MAJOR_VERSION_MASK).