Pregunta

Sé que puedo llamar a la función API GetVersionEx Win32 para recuperar la versión de Windows.En la mayoría de los casos, el valor devuelto refleja la versión de mi Windows, pero a veces no es así.

Si un usuario ejecuta mi aplicación bajo la capa de compatibilidad, GetVersionEx no informará la versión real sino la versión impuesta por la capa de compatibilidad.Por ejemplo, si estoy ejecutando Vista y ejecuto mi programa en modo de compatibilidad "Windows NT 4", GetVersionEx no devolverá la versión 6.0 sino la 4.0.

¿Hay alguna manera de evitar este comportamiento y obtener una versión real de Windows?

¿Fue útil?

Solución

El mejor enfoque que conozco es verificar si una API específica se exporta desde alguna DLL.Cada nueva versión de Windows agrega nuevas funciones y al verificar la existencia de esas funciones se puede saber en qué sistema operativo se está ejecutando la aplicación.Por ejemplo, Vista exporta Obtener información localEx desde kernel32.dll mientras que Windows anteriores no lo hacían.

Para abreviar la historia, aquí hay una lista que contiene solo exportaciones desde 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  

Escribir la función para determinar la versión real del sistema operativo es simple;simplemente pase del sistema operativo más nuevo al más antiguo y use Obtener dirección Proc para comprobar las API exportadas.Implementar esto en cualquier idioma debería ser trivial.

El siguiente código en Delphi fue extraído del archivo 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 }

--- actualizado el 9 de octubre de 2009

Resulta que resulta muy difícil realizar una detección de sistema operativo "no documentado" en Vista SP1 y superior.Una mirada a la Cambios de API muestra que todas las funciones de Windows 2008 también están implementadas en Vista SP1 y que todas las funciones de Windows 7 también están implementadas en Windows 2008 R2.Demasiado :(

--- fin de la actualización

FWIW, este es un problema que encontré en la práctica.Nosotros (la empresa para la que trabajo) tenemos un programa que no estaba realmente preparado para Vista cuando se lanzó Vista (y algunas semanas después...).Tampoco funcionaba bajo la capa de compatibilidad.(Algunos problemas de DirectX.No preguntes.)

No queríamos que usuarios demasiado inteligentes para su propio bien ejecutaran esta aplicación en Vista (modo de compatibilidad o no), así que tuve que encontrar una solución (un tipo más inteligente que yo me indicó la dirección correcta;lo anterior no es mi creación).Ahora lo publico para su placer y para ayudar a todas las pobres almas que tendrán que resolver este problema en el futuro.Google, indexa este artículo.

Si tiene una solución mejor (o una actualización y/o solución para la mía), publique una respuesta aquí...

Otros consejos

CONSULTA WMI:

"Select * from Win32_OperatingSystem"

EDITAR:En realidad mejor sería:

"Select Version from Win32_OperatingSystem"

Podrías implementar esto en Delphi así:

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;

¿Qué tal obtener la versión de un archivo del sistema?

El mejor archivo sería kernel32.dll, ubicado en %WINDIR%\System32\kernel32.dll.

Existen API para obtener la versión del archivo.p.ej:Estoy usando Windows XP -> "5.1.2600.5512 (xpsp.080413-2111)"

Otra solución:

lea la siguiente entrada del registro:

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

u otras claves de

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion

Almacén de versión real en bloque PEB de información de proceso.

Ejemplo de aplicación 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.

Lo siguiente me funciona en Windows 10 sin el GUID de Windows 10 que figura en el manifiesto de la aplicación:

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.

Actualizar: NetWkstaGetInfo() Probablemente también funcionaría, similar a 'NetServerGetInfo()`, pero aún no lo he probado.

Nota: Gabr pregunta acerca de un enfoque que pueda superar las limitaciones de GetVersionEx.El código JCL utiliza GetVersionEx y, por tanto, está sujeto a la capa de compatibilidad.Esta información es solo para personas que no necesitan omitir la capa de compatibilidad.

Usando Jedi JCL, puede agregar la unidad JclSysInfo y llamar a la función GetWindowsVersion.Devuelve un tipo enumerado TWindowsVersion.

Actualmente, JCL contiene todas las versiones de Windows enviadas y se modifica cada vez que Microsoft envía una nueva versión de Windows en una caja:

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

Si desea saber si está ejecutando Windows 7 de 64 bits en lugar de 32 bits, llame JclSysInfo.IsWindows64.

Tenga en cuenta que JCL también maneja ediciones, como Pro, Ultimate, etc.Para eso, llame a GetWindowsEdition y devuelve uno de estos:

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

Por interés histórico, también puede consultar la edición de nivel NT con la función NtProductType, que devuelve:

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

Tenga en cuenta que arriba se detectan "N ediciones".Esa es una versión de Windows para la UE (Europa), creada debido a las regulaciones antimonopolio de la UE.Esa es una gradación de detección bastante buena dentro del JCL.

Aquí hay una función de muestra que lo ayudará a detectar Vista y hacer algo especial cuando esté en Vista.

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

Tenga en cuenta que si desea realizar una comprobación "mayor que", debería utilizar otras técnicas.También tenga en cuenta que la verificación de versiones a menudo puede ser una fuente de fallas futuras.Normalmente he optado por advertir a los usuarios y continuar, para que mi código binario no se convierta en la fuente real de rotura en el futuro.

Recientemente intenté instalar una aplicación y el instalador comprobó el espacio libre en mi disco y no la instaló porque tenía más de 2 gigabytes de espacio libre.El valor entero con signo de 32 bits en el instalador se volvió negativo, rompiendo el instalador.Tuve que instalarlo en una VM para que funcionara.Agregar "código inteligente" a menudo hace que su aplicación sea "más estúpida".Sé cauteloso.

Por cierto, descubrí que desde la línea de comando, puedes ejecutar WMIC.exe y escribir path Win32_OperatingSystem (El "Seleccionar * de Win32_OperatingSystem" no funcionó para mí).En el futuro, quizás JCL podría ampliarse para utilizar la información WMI.

Básicamente para responder a la pregunta duplicada: Obtener versiones principales, secundarias y de compilación del sistema operativo para Windows 8.1 y posteriores en Delphi 2007

A partir de W2K puedes usar NetServerObtener información.NetServerGetInfo devuelve la información correcta en W7 y W8.1, no se puede realizar la prueba en 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 sobre el uso de NetServerGetInfo(), que todavía funciona en Windows 10 (10240.th1_st1)...

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

sv101_version_major

El número de versión principal y el tipo de servidor.

El número de versión de lanzamiento principal del sistema operativo se especifica en los 4 bits menos significativos.El tipo de servidor se especifica en los 4 bits más significativos.La máscara de bits de Major_Version_MASK definida en el encabezado LMServer.h {0x0f} debe ser utilizado por una aplicación para obtener el número de versión principal de este miembro.

En otras palabras, (sv101_version_major & MAJOR_VERSION_MASK).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top