Pergunta

Eu sei que posso chamar a função da API GetVersionEx Win32 para recuperar a versão do Windows.Na maioria dos casos, o valor retornado reflete a versão do meu Windows, mas às vezes não é assim.

Se um usuário executar meu aplicativo na camada de compatibilidade, GetVersionEx não reportará a versão real, mas a versão imposta pela camada de compatibilidade.Por exemplo, se eu estiver executando o Vista e executar meu programa no modo de compatibilidade "Windows NT 4", GetVersionEx não retornará a versão 6.0, mas a 4.0.

Existe uma maneira de contornar esse comportamento e obter a versão verdadeira do Windows?

Foi útil?

Solução

A melhor abordagem que conheço é verificar se uma API específica é exportada de alguma DLL.Cada nova versão do Windows adiciona novas funções e, ao verificar a existência dessas funções, é possível saber em qual sistema operacional o aplicativo está sendo executado.Por exemplo, o Vista exporta GetLocaleInfoEx de kernel32.dll enquanto o Windows anterior não.

Para resumir a longa história, aqui está uma lista contendo apenas exportações 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  

Escrever a função para determinar a versão real do sistema operacional é simples;basta prosseguir do sistema operacional mais recente para o mais antigo e usar GetProcAddress para verificar APIs exportadas.Implementar isso em qualquer linguagem deve ser trivial.

O seguinte código em Delphi foi extraído do arquivo gratuito DSiWin32 biblioteca):

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 }

--- atualizado em 09/10/2009

Acontece que fica muito difícil fazer uma detecção de sistema operacional "não documentado" no Vista SP1 e superior.Uma olhada no Mudanças de API mostra que todas as funções do Windows 2008 também são implementadas no Vista SP1 e que todas as funções do Windows 7 também são implementadas no Windows 2008 R2.Muito ruim :(

--- fim da atualização

FWIW, este é um problema que encontrei na prática.Nós (a empresa para a qual trabalho) temos um programa que não estava realmente pronto para o Vista quando o Vista foi lançado (e algumas semanas depois disso...).Também não estava funcionando na camada de compatibilidade.(Alguns problemas de DirectX.Não pergunte.)

Não queríamos que usuários muito inteligentes para seu próprio bem executassem esse aplicativo no Vista - modo de compatibilidade ou não - então tive que encontrar uma solução (um cara mais inteligente do que eu me indicou a direção certa;o material acima não é ideia minha).Agora estou postando para seu prazer e para ajudar todas as pobres almas que terão que resolver este problema no futuro.Google, indexe este artigo!

Se você tiver uma solução melhor (ou uma atualização e/ou correção para a minha), poste uma resposta aqui ...

Outras dicas

Consulta WMI:

"Select * from Win32_OperatingSystem"

EDITAR:Na verdade melhor seria:

"Select Version from Win32_OperatingSystem"

Você poderia implementar isso no Delphi assim:

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 tal obter a versão de um arquivo de sistema?

O melhor arquivo seria kernel32.dll, localizado em %WINDIR%\System32\kernel32.dll.

Existem APIs para obter a versão do arquivo.por exemplo:Estou usando o Windows XP -> "5.1.2600.5512 (xpsp.080413-2111)"

Outra solução:

leia a seguinte entrada de registro:

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

ou outras chaves de

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion

armazenamento de versão real no bloco PEB de informações do processo.

Amostra para aplicativo 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.

O seguinte funciona para mim no Windows 10 sem o GUID do Windows 10 listado no manifesto do aplicativo:

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.

Atualizar: NetWkstaGetInfo() provavelmente também funcionaria, semelhante a 'NetServerGetInfo()`, mas ainda não tentei.

Observação: Gabr está perguntando sobre uma abordagem que possa contornar as limitações do GetVersionEx.O código JCL usa GetVersionEx e, portanto, está sujeito à camada de compatibilidade.Esta informação é apenas para pessoas que não precisam ignorar a camada de compatibilidade.

Usando o Jedi JCL, você pode adicionar a unidade JclSysInfo e chamar a função GetWindowsVersion.Ele retorna um tipo enumerado TWindowsVersion.

Atualmente, o JCL contém todas as versões fornecidas do Windows e é alterado sempre que a Microsoft envia uma nova versão do Windows em uma caixa:

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

Se você quiser saber se está executando o Windows 7 de 64 bits em vez de 32 bits, ligue JclSysInfo.IsWindows64.

Observe que JCL também lida com edições, como Pro, Ultimate, etc.Para isso, chame GetWindowsEdition e ele retornará um destes:

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 interesse histórico, você também pode verificar a edição no nível NT com a função NtProductType, ela retorna:

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

Observe que "N edições" são detectadas acima.Essa é uma versão do Windows para a UE (Europa), criada devido às regulamentações antitruste da UE.Essa é uma gradação muito boa de detecção dentro do JCL.

Aqui está um exemplo de função que o ajudará a detectar o Vista e a fazer algo especial quando estiver no Vista.

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

Observe que se você quiser fazer uma verificação "maior que", deverá usar outras técnicas.Observe também que a verificação de versão muitas vezes pode ser uma fonte de falhas futuras.Geralmente opto por avisar os usuários e continuar, para que meu código binário não se torne a verdadeira fonte de quebra no futuro.

Recentemente tentei instalar um aplicativo e o instalador verificou o espaço livre na minha unidade e não instalou, porque eu tinha mais de 2 gigabytes de espaço livre.O valor inteiro assinado de 32 bits no instalador tornou-se negativo, interrompendo o instalador.Tive que instalá-lo em uma VM para fazê-lo funcionar.Adicionar "código inteligente" geralmente torna seu aplicativo mais "estúpido".Seja cauteloso.

Aliás, descobri que na linha de comando você pode executar WMIC.exe e digitar path Win32_OperatingSystem (O "Select * from Win32_OperatingSystem" não funcionou para mim).No futuro, talvez o JCL possa ser estendido para usar as informações do WMI.

Essencialmente para responder a Q duplicada: Obtendo versões principais, secundárias e de compilação do sistema operacional para Windows 8.1 e superior no Delphi 2007

Começando com W2K você pode usar NetServerGetInfo.NetServerGetInfo retorna as informações corretas em W7 e W8.1, não é possível testar em 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;

Uma observação sobre o uso de NetServerGetInfo(), que ainda funciona no Windows 10 (10240.th1_st1)...

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

sv101_version_major

O número da versão principal e o tipo de servidor.

O número principal da versão de lançamento do sistema operacional é especificado nos 4 bits menos significativos.O tipo de servidor é especificado nos 4 bits mais significativos.O major_version_mask bitmask definido no cabeçalho lmServer.h {0x0f} deve ser usado por um aplicativo para obter o número principal da versão deste membro.

Em outras palavras, (sv101_version_major & MAJOR_VERSION_MASK).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top