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 ?

Était-ce utile?

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top