Domanda

So che posso chiamare il GetVersionEx funzione API Win32 per recuperare la versione di Windows.Nella maggior parte dei casi, viene restituito il valore riflette la versione di Windows, ma a volte non è così.

Se un utente esegue la mia domanda sotto il livello di compatibilità, quindi GetVersionEx non essere segnalato la versione reale, ma la versione imposto dal livello di compatibilità.Per esempio, se io sono in esecuzione Vista ed eseguire il mio programma "Windows NT 4" modalità di compatibilità, GetVersionEx non ritorno versione 6.0 ma la versione 4.0.

C'è un modo per aggirare questo comportamento e ottenere la vera versione di Windows?

È stato utile?

Soluzione

L'approccio migliore che conosco è quello di verificare se la specifica API esportato da alcune DLL.Ogni nuova versione di Windows aggiunge nuove funzioni e verificando l'esistenza di tali funzioni si può dire che OS l'applicazione è in esecuzione.Per esempio, Vista esportazioni GetLocaleInfoEx da kernel32.dll mentre le precedenti Windowses non.

Per tagliare la lunga storia breve, qui è una lista contenente solo le esportazioni dal 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  

Scrivere la funzione di determinare la vera versione del sistema operativo è semplice;basta procedere dal sistema operativo più recente al più vecchio e utilizzare GetProcAddress per verificare esportato Api.L'implementazione di questo in qualsiasi lingua dovrebbe essere banale.

Il codice riportato di seguito in Delphi è stato estratto gratuito DSiWin32 libreria:

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 }

--- aggiornato 2009-10-09

Si scopre che diventa molto difficile fare un "senza documenti" OS detection su Vista SP1 e versioni successive.Uno sguardo al Modifiche alle API mostra che tutte le Finestre 2008 funzioni sono state implementate anche in Vista SP1 e Windows 7 funzioni sono implementate in Windows 2008 R2.Troppo male :(

--- end di aggiornamento

FWIW, questo è un problema che ho incontrato nella pratica.Noi (l'azienda per cui lavoro) dispone di un programma che non era davvero pronto per Vista quando Vista è stato rilasciato (e alcune settimane dopo di che ...).Non stava lavorando sotto il livello di compatibilità sia.(Alcuni problemi con DirectX.Non chiedere.)

Non volevamo troppo-smart-per-loro-proprio-buona agli utenti di eseguire questa applicazione su Vista - la modalità di compatibilità o non - così ho dovuto trovare una soluzione (un ragazzo più intelligente di me mi hanno indirizzato nella giusta direzione;la roba di cui sopra non è una mia invenzione).Ora sto postando per il vostro piacere e per aiutare tutte le povere anime che dovrà risolvere questo problema in futuro.Google, si prega di indice di questo articolo!

Se avete una soluzione migliore (o un aggiornamento e/o di correzione per la mia), si prega di inviare una risposta qui ...

Altri suggerimenti

WMI QUery:

"Select * from Win32_OperatingSystem"

EDIT:In realtà sarebbe meglio:

"Select Version from Win32_OperatingSystem"

Si potrebbe implementare questo in Delphi in questo modo:

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;

Come su come ottenere la versione di un file di sistema?

Il file migliore sarebbe kernel32.dll situato in %WINDIR%\System32\kernel32.dll.

Ci sono le Api per ottenere la versione del file.ad esempio:Sto utilizzando Windows XP -> "5.1.2600.5512 (xpsp.080413-2111)"

Un'altra soluzione:

leggi la seguente voce del registro di sistema:

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

o altri tasti da

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion

versione reale archivio PEB blocco di informazioni di processo.

Esempio di applicazione di Win32 (Delphi Codice)

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.

Le seguenti opere per me in Windows 10 senza Windows 10 GUID elencate nel manifesto dell'applicazione:

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.

Aggiornamento: NetWkstaGetInfo() probabilmente anche il lavoro, simile a 'NetServerGetInfo()`, ma non ho prova di sicurezza.

Nota: Gabr chiede su un approccio che può bypassare le limitazioni di GetVersionEx.JCL codice utilizza GetVersionEx, ed è quindi soggetto a livello di compatibilità.Questa informazione è per le persone che non hanno bisogno di ignorare il livello di compatibilità, solo.

Utilizzando il Jedi JCL, è possibile aggiungere unità JclSysInfo, e la funzione di chiamata GetWindowsVersion.Restituisce un tipo enumerato TWindowsVersion.

Attualmente JCL contiene tutti spediti versioni di windows, e viene cambiato ogni volta che Microsoft disponibile una nuova versione di Windows in una scatola:

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

Se vuoi sapere se sei in esecuzione a 64 bit di windows 7 invece di 32 bit, quindi chiamare JclSysInfo.IsWindows64.

Nota che JCL allso maniglie Edizioni, come Pro, Ultimate, etc.Per tale chiamata GetWindowsEdition, e restituisce uno di questi:

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

Per interesse storico, è possibile controllare il NT a livello di edizione con la NtProductType funzione, restituisce:

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

Nota che "N edizioni" sono rilevati in precedenza.Che la UE (Europa) versione di Windows, creato a causa di UE le regole antitrust.Che bella la fine gradazione di rilevamento all'interno del JCL.

Ecco un esempio di una funzione che consente di rilevare Vista, e fare qualcosa di speciale quando su Vista.

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

Nota che se vuoi fare "maggiore di" controllo, allora si dovrebbe usare altre tecniche.Inoltre, si noti che la versione di controllo può spesso essere fonte di future rotture.Solitamente ho scelto di avvisare gli utenti e continuare, in modo che il mio codice binario non diventi la fonte reale di rottura in futuro.

Di recente ho provato ad installare un app, e il programma di installazione controllato il mio spazio libero su disco, e non installare, perché ho avuto più di 2 gigabyte di spazio libero.Il numero intero a 32 bit firmato valore in il programma di installazione è diventato negativo, rompendo il programma di installazione.Ho dovuto installarlo in una macchina virtuale per farlo funzionare.L'aggiunta di "codice smart", spesso rende l'app "stupidi".Essere cauti.

Per inciso, ho scoperto che da riga di comando, è possibile eseguire WMIC.exe e tipo di path Win32_OperatingSystem ("Select * from Win32_OperatingSystem" non ha funzionato per me).In futuro, forse, JCL potrebbe essere estesa ad utilizzare le informazioni di WMI.

Essenzialmente per rispondere a duplicare D: Ottenere OS (maggiore, minore, e costruire le versioni per Windows 8.1 e in Delphi 2007

Partenza con W2K è possibile utilizzare NetServerGetInfo.NetServerGetInfo restituisce le informazioni corrette su W7 e W8.1, in grado di testare su 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;

Una nota sull'utilizzo di NetServerGetInfo(), che funziona ancora su Windows 10 (10240.th1_st1)...

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

sv101_version_major

Il numero di versione principale e il tipo di server.

La major release numero di versione del sistema operativo è specificato in meno significativo di 4 bit.Il tipo di server è specificato nel più significativi di 4 bit.Il MAJOR_VERSION_MASK maschera di bit definito nel Lmserver.h header {0x0F} deve essere utilizzato da un'applicazione per ottenere il numero di versione principale di questo membro.

In altre parole, (sv101_version_major & MAJOR_VERSION_MASK).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top