Comment détecter la vraie version de Windows?

Je sais que je peux appeler la fonction GetVersionEx Win32 API pour récupérer la version de Windows. Dans la plupart des cas, la valeur renvoyée reflète la version de Windows, mais ce n’est parfois pas le cas.

Si un utilisateur exécute mon application sous la couche de compatibilité, GetVersionEx ne signalera pas la version réelle, mais la version appliquée par la couche de compatibilité. Par exemple, si j’exécute Vista et exécute mon programme en mode de compatibilité “Windows NT 4”, GetVersionEx ne renverra pas la version 6.0 mais 4.0.

Existe-t-il un moyen de contourner ce comportement et d’obtenir une vraie version de Windows?

La meilleure approche que je connaisse est de vérifier si une API spécifique est exscope de certaines DLL. Chaque nouvelle version de Windows ajoute de nouvelles fonctions et en vérifiant l’existence de ces fonctions, on peut déterminer sur quel système d’exploitation l’application est exécutée. Par exemple, Vista exporte GetLocaleInfoEx à partir de kernel32.dll, contrairement aux versions précédentes de Windows.

En résumé, voici une liste contenant uniquement les exportations de kernel32.dll.

 > * fonction: implémentée dans *  
 > 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  
 IsDebuggerPrésent: 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  
 Bip: Vista, Server 2003, XP SP1, XP, 2000, ME, 98, 95 OSR2, 95  

L’écriture de la fonction pour déterminer la version réelle du système d’exploitation est simple. Il suffit de passer du plus récent système d’exploitation au plus ancien et d’utiliser GetProcAddress pour vérifier les API exscopes. Implémenter cela dans n’importe quelle langue devrait être sortingvial.

Le code suivant dans Delphi a été extrait de la bibliothèque gratuite 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: ssortingng): 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 } 

— mis à jour 2009-10-09

Il s’avère très difficile d’effectuer une détection de système d’exploitation “non documentée” sur Vista SP1 et supérieur. Un examen des modifications de l’ API montre que toutes les fonctions de Windows 2008 sont également implémentées dans Vista SP1 et que toutes les fonctions de Windows 7 sont également implémentées dans Windows 2008 R2. Dommage 🙁

— fin de mise à jour

FWIW, c’est un problème que j’ai rencontré dans la pratique. Nous (la société pour laquelle je travaille) avons un programme qui n’était pas vraiment prêt pour Vista lors de la sortie de Vista (et quelques semaines plus tard…). Il ne fonctionnait pas non plus sous la couche de compatibilité. (Certains problèmes DirectX. Ne demandez pas.)

Nous ne voulions pas que les utilisateurs trop intelligents pour eux-mêmes exécutent cette application sur Vista – le mode de compatibilité ou non – et j’ai donc dû trouver une solution (un type plus intelligent que moi m’a orienté dans la bonne direction; les choses ci-dessus ne sont pas mon idée originale). Maintenant je le poste pour votre plaisir et pour aider toutes les âmes pauvres qui devront résoudre ce problème dans le futur. Google, veuillez indexer cet article!

Si vous avez une meilleure solution (ou une mise à niveau et / ou un correctif pour le mien), veuillez poster une réponse ici …

WMI QUery:

 "Select * from Win32_OperatingSystem" 

EDIT: En fait, mieux serait:

 "Select Version from Win32_OperatingSystem" 

Vous pourriez implémenter ceci dans Delphi comme ceci:

 function OperatingSystemDisplayName: ssortingng; function GetWMIObject(const objectName: ssortingng): 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 VarToSsortingng(const Value: OleVariant): ssortingng; begin if VarIsStr(Value) then begin Result := Trim(Value); end else begin Result := ''; end; end; function FullVersionSsortingng(const Item: OleVariant): ssortingng; var Caption, ServicePack, Version, Architecture: ssortingng; begin Caption := VarToSsortingng(Item.Caption); ServicePack := VarToSsortingng(Item.CSDVersion); Version := VarToSsortingng(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; 

Que diriez-vous d’obtenir la version d’un fichier système?

Le meilleur fichier serait kernel32.dll, situé dans% WINDIR% \ System32 \ kernel32.dll.

Il existe des API pour obtenir la version du fichier. par exemple: J’utilise Windows XP -> “5.1.2600.5512 (xpsp.080413-2111)”

Une autre solution:

lisez l’entrée de registre suivante:

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

ou d’autres clés de

 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion 

version réelle stocker sur le bloc PEB des informations de processus.

Exemple d’application Win32 (code 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; GdiDCAtsortingbuteList: 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. 

Les éléments suivants fonctionnent pour moi dans Windows 10 sans le GUID Windows 10 répertorié dans le manifeste d’application:

 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. 

Mise à jour : NetWkstaGetInfo() fonctionnerait probablement aussi, comme NetServerGetInfo (), mais je ne l’ai pas encore essayé.

Remarque: Gabr demande une approche qui peut contourner les limitations de GetVersionEx . Le code JCL utilise GetVersionEx et est donc soumis à la couche de compatibilité. Ces informations sont destinées aux personnes qui n’ont pas besoin de contourner la couche de compatibilité uniquement.

En utilisant le Jedi JCL, vous pouvez append l’unité JclSysInfo et appeler la fonction GetWindowsVersion . Il renvoie un type énuméré TWindowsVersion.

Actuellement, JCL contient toutes les versions de Windows livrées et est modifié chaque fois que Microsoft fournit une nouvelle version de Windows dans une boîte:

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

Si vous voulez savoir si vous exécutez Windows 7 64 bits au lieu de 32 bits, appelez JclSysInfo.IsWindows64 .

Notez que JCL gère également les éditions, telles que Pro, Ultimate, etc. Pour cet appel GetWindowsEdition, et il renvoie l’un de ces éléments:

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

Pour l’intérêt historique, vous pouvez également vérifier l’édition de niveau NT avec la fonction NtProductType, elle renvoie:

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

Notez que “N éditions” sont détectées ci-dessus. C’est une version de Windows de l’UE (Europe), créée en raison de la réglementation anti-trust de l’UE. C’est une très bonne gradation de détection dans le JCL.

Voici un exemple de fonction qui vous aidera à détecter Vista et à faire quelque chose de spécial sur Vista.

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

Notez que si vous souhaitez effectuer une vérification “supérieure à”, vous devez simplement utiliser d’autres techniques. Notez également que la vérification de version peut souvent être une source de rupture future. J’ai généralement choisi d’avertir les utilisateurs et de continuer, afin que mon code binary ne devienne pas la source réelle de rupture à l’avenir.

Récemment, j’ai essayé d’installer une application et le programme d’installation a vérifié l’espace disque disponible sur mon lecteur et ne l’a pas installé, car j’avais plus de 2 Go d’espace libre. La valeur 32 bits signée dans le programme d’installation est devenue négative, interrompant le programme d’installation. Je devais l’installer sur une VM pour la faire fonctionner. L’ajout de “code intelligent” rend souvent votre application “plus stupide”. Se méfier.

Incidemment, j’ai trouvé que depuis la ligne de commande, vous pouvez exécuter WMIC.exe et taper le path Win32_OperatingSystem (le “Select * from Win32_OperatingSystem” ne fonctionnait pas pour moi). À l’avenir, peut-être que JCL pourrait être étendu pour utiliser les informations WMI.

Essentiellement pour répondre aux doublons Q: Obtention des versions majeures, mineures et de construction du système d’exploitation pour Windows 8.1 et versions ultérieures dans Delphi 2007

À partir de W2K, vous pouvez utiliser NetServerGetInfo . NetServerGetInfo renvoie les informations correctes sur W7 et W8.1, sans pouvoir tester sur W10.

 function GetWinVersion: ssortingng; 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; 

Une remarque sur l’utilisation de NetServerGetInfo (), qui fonctionne toujours sous Windows 10 (10240.th1_st1) …

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

sv101_version_major

Le numéro de version principal et le type de serveur.

Le numéro de version majeure du système d’exploitation est spécifié dans les 4 bits les moins significatifs. Le type de serveur est spécifié dans les 4 bits les plus significatifs. Le masque MAJOR_VERSION_MASK défini dans l’en-tête Lmserver.h {0x0F} doit être utilisé par une application pour obtenir le numéro de version majeur de ce membre.

En d’autres termes, (sv101_version_major & MAJOR_VERSION_MASK).