Comment détecter la vraie version de Windows ?
Question
Je sais que je peux appeler la fonction API GetVersionEx Win32 pour récupérer la version de Windows.Dans la plupart des cas, la valeur renvoyée reflète la version de mon Windows, mais parfois ce n'est 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'utilise Vista et que j'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 la véritable version de Windows ?
La solution
La meilleure approche que je connaisse est de vérifier si une API spécifique est exportée à partir d'une DLL.Chaque nouvelle version de Windows ajoute de nouvelles fonctions et en vérifiant l'existence de ces fonctions, on peut savoir sur quel système d'exploitation l'application s'exécute.Par exemple, Vista exporte GetLocaleInfoEx à partir de kernel32.dll alors que les Windows précédents ne le faisaient pas.
Pour faire court, voici une de ces listes contenant uniquement les exportations de kernel32.dll.
> *function: implemented in* > 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 Beep: Vista, Server 2003, XP SP1, XP, 2000, ME, 98, 95 OSR2, 95
Écrire la fonction pour déterminer la version réelle du système d'exploitation est simple ;passez simplement du système d'exploitation le plus récent au plus ancien et utilisez ObtenirProcAddress pour vérifier les API exportées.L’implémentation dans n’importe quelle langue devrait être triviale.
Le code suivant dans Delphi a été extrait du fichier gratuit DSiWin32 bibliothèque):
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 }
--- mis à jour le 09/10/2009
Il s'avère qu'il devient très difficile d'effectuer une détection de système d'exploitation "non documentée" sur Vista SP1 et supérieur.Un regard sur 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 la mise à jour
FWIW, c'est un problème que j'ai rencontré dans la pratique.Nous (l'entreprise pour laquelle je travaille) avons un programme qui n'était pas vraiment prêt pour Vista lorsque Vista est sorti (et quelques semaines après...).Cela ne fonctionnait pas non plus sous la couche de compatibilité.(Quelques problèmes DirectX.Ne demandez pas.)
Nous ne voulions pas du tout que des utilisateurs trop intelligents pour leur propre bien exécutent cette application sur Vista - en mode de compatibilité ou non - j'ai donc dû trouver une solution (un gars plus intelligent que moi m'a indiqué la bonne direction ;les éléments ci-dessus ne sont pas mon idée).Maintenant, je le publie pour votre plaisir et pour aider toutes les pauvres âmes qui devront résoudre ce problème à l'avenir.Google, veuillez indexer cet article !
Si vous avez une meilleure solution (ou une mise à niveau et/ou un correctif pour la mienne), veuillez poster une réponse ici...
Autres conseils
Requête WMI :
"Select * from Win32_OperatingSystem"
MODIFIER:En fait, mieux serait :
"Select Version from Win32_OperatingSystem"
Vous pouvez implémenter cela dans Delphi comme ceci :
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;
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
magasin de versions réelles sur le bloc PEB d'informations sur le processus.
Exemple pour l'application Win32 (Delphi Code)
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.
Ce qui suit fonctionne pour moi sous Windows 10 sans le GUID Windows 10 répertorié dans le manifeste de l'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, similaire à 'NetServerGetInfo()`, mais je ne l'ai pas encore essayé.
Note: Gabr s'interroge sur une approche qui pourrait contourner les limites de GetVersionEx
.Le code JCL utilise GetVersionEx et est donc soumis à la couche de compatibilité.Ces informations sont uniquement destinées aux personnes qui n’ont pas besoin de contourner la couche de compatibilité.
À l'aide du JCL Jedi, vous pouvez ajouter 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 expédie 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 utilisez Windows 7 64 bits au lieu de 32 bits, appelez JclSysInfo.IsWindows64
.
Notez que JCL gère également les éditions, comme Pro, Ultimate, etc.Pour cela, appelez 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 un intérêt historique, vous pouvez également vérifier l'édition au niveau NT avec la fonction NtProductType, elle renvoie :
TNtProductType = (ptUnknown, ptWorkStation, ptServer, ptAdvancedServer,
ptPersonal, ptProfessional, ptDatacenterServer,
ptEnterprise, ptWebEdition);
Notez que les « éditions N » sont détectées ci-dessus.Il s'agit d'une version européenne (européenne) de Windows, créée en raison des réglementations antitrust de l'UE.C'est une gradation assez fine de détection à l'intérieur du JCL.
Voici un exemple de fonction qui vous aidera à détecter Vista et à faire quelque chose de spécial sous 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érieur à », vous devez simplement utiliser d'autres techniques.Notez également que la vérification de version peut souvent être une source de casse future.J'ai généralement choisi de prévenir les utilisateurs et de continuer, afin que mon code binaire ne devienne pas la véritable source de casse dans le futur.
Récemment, j'ai essayé d'installer une application et le programme d'installation a vérifié l'espace libre sur mon disque et n'a pas voulu l'installer, car j'avais plus de 2 Go d'espace libre.La valeur signée entière de 32 bits dans le programme d'installation est devenue négative, interrompant le programme d'installation.J'ai dû l'installer sur une VM pour le faire fonctionner.L'ajout de "code intelligent" rend souvent votre application "stupide".Se méfier.
Par ailleurs, j'ai découvert qu'à partir de la ligne de commande, vous pouvez exécuter WMIC.exe et taper path Win32_OperatingSystem
(Le "Select * from Win32_OperatingSystem" n'a pas fonctionné pour moi).À l'avenir, JCL pourrait peut-être être étendu pour utiliser les informations WMI.
Essentiellement pour répondre à la question en double : Obtenir les versions majeures, mineures et de build 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, impossible de tester sur W10.
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;
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 majeure et le type de serveur.
Le numéro de version 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 importants.Le bitmask 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).