Вопрос

Я знаю, что могу вызвать функцию GetVersionEx Win32 API, чтобы получить версию Windows.В большинстве случаев возвращаемое значение отражает версию моей Windows, но иногда это не так.

Если пользователь запускает мое приложение на уровне совместимости, GetVersionEx будет сообщать не о реальной версии, а о версии, реализованной на уровне совместимости.Например, если я использую Vista и запускаю свою программу в режиме совместимости с «Windows NT 4», GetVersionEx вернет не версию 6.0, а 4.0.

Есть ли способ обойти это поведение и получить настоящую версию Windows?

Это было полезно?

Решение

Лучший известный мне подход — проверить, экспортируется ли конкретный API из какой-либо DLL.Каждая новая версия Windows добавляет новые функции, и, проверив наличие этих функций, можно определить, в какой ОС работает приложение.Например, Vista экспортирует GetLocaleInfoEx из kernel32.dll, в то время как в предыдущих версиях Windows этого не было.

Короче говоря, вот один такой список, содержащий только экспорт из 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  

Написать функцию для определения реальной версии ОС несложно;просто перейдите от новейшей ОС к самой старой и используйте GetProcAddress для проверки экспортированных API.Реализация этого на любом языке должна быть тривиальной.

Следующий код в Delphi был извлечен из бесплатного ДСиВин32 библиотека):

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 }

--- обновлено 9 октября 2009 г.

Оказывается, на Vista SP1 и выше очень сложно обнаружить «недокументированную» ОС.Взгляните на Изменения API показывает, что все функции Windows 2008 также реализованы в Vista SP1 и что все функции Windows 7 также реализованы в Windows 2008 R2.Очень жаль :(

--- конец обновления

Кстати, с этой проблемой я столкнулся на практике.У нас (компании, в которой я работаю) есть программа, которая на самом деле не была готова для Vista на момент выпуска Vista (и через несколько недель после этого...).На уровне совместимости тоже не работало.(Некоторые проблемы с DirectX.Не спрашивай.)

Мы вообще не хотели, чтобы слишком умные пользователи запускали это приложение в Vista — в режиме совместимости или нет — поэтому мне пришлось найти решение (парень умнее меня указал мне правильное направление;материал выше не мое детище).Сейчас выкладываю это для вашего удовольствия и в помощь всем беднягам, которым придется решать эту проблему в будущем.Google, пожалуйста, проиндексируйте эту статью!

Если у вас есть лучшее решение (или обновление и/или исправление для моего), опубликуйте ответ здесь...

Другие советы

WMI-запрос:

"Select * from Win32_OperatingSystem"

РЕДАКТИРОВАТЬ:На самом деле лучше было бы:

"Select Version from Win32_OperatingSystem"

Вы можете реализовать это в Delphi следующим образом:

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;

Как насчет получения версии системного файла?

Лучшим файлом будет kernel32.dll, расположенный в %WINDIR%\System32\kernel32.dll.

Существуют API для получения версии файла.например:Я использую Windows XP -> «5.1.2600.5512 (xpsp.080413-2111)»

Другое решение:

прочитайте следующую запись реестра:

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

или другие ключи от

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion

реальная версия хранится в блоке PEB информации о процессе.

Пример приложения Win32 (код 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.

У меня в Windows 10 без GUID Windows 10, указанного в манифесте приложения, работает следующее:

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.

Обновлять: NetWkstaGetInfo() вероятно, тоже будет работать, аналогично «NetServerGetInfo()», но я еще не пробовал.

Примечание: Габр спрашивает о подходе, который может обойти ограничения GetVersionEx.Код JCL использует GetVersionEx и, следовательно, подлежит уровню совместимости.Эта информация предназначена только для людей, которым не нужно обходить уровень совместимости.

Используя Jedi JCL, вы можете добавить модуль JclSysInfo и вызвать функцию GetWindowsVersion.Он возвращает перечислимый тип TWindowsVersion.

В настоящее время JCL содержит все поставляемые версии Windows и меняется каждый раз, когда Microsoft поставляет новую версию Windows в коробке:

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

Если вы хотите узнать, используете ли вы 64-битную версию Windows 7 вместо 32-битной, позвоните JclSysInfo.IsWindows64.

Обратите внимание, что JCL также работает с такими выпусками, как Pro, Ultimate и т. д.Для этого вызовите GetWindowsEdition, и он вернет одно из следующих значений:

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

Для исторического интереса вы также можете проверить версию уровня NT с помощью функции NtProductType, она возвращает:

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

Обратите внимание, что выше обнаружено «N редакций».Это версия Windows для ЕС (Европы), созданная в соответствии с антимонопольными правилами ЕС.Это довольно тонкая степень обнаружения внутри JCL.

Вот пример функции, которая поможет вам обнаружить Vista и сделать что-то особенное в Vista.

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

Обратите внимание: если вы хотите выполнить проверку «больше чем», вам следует просто использовать другие методы.Также обратите внимание, что проверка версий часто может стать источником будущих сбоев.Обычно я предпочитаю предупредить пользователей и продолжить, чтобы мой двоичный код не стал реальным источником поломок в будущем.

Недавно я попытался установить приложение, но установщик проверил свободное место на моем диске и не смог установить, поскольку у меня было более 2 гигабайт свободного места.32-битное целое число со знаком в программе установки стало отрицательным, что привело к поломке программы установки.Мне пришлось установить его на виртуальную машину, чтобы заставить его работать.Добавление «умного кода» часто делает ваше приложение «глупее».Будьте осторожны.

Кстати, я обнаружил, что из командной строки можно запустить WMIC.exe и набрать path Win32_OperatingSystem («Выбрать * из Win32_OperatingSystem» у меня не сработало).В будущем, возможно, JCL можно будет расширить для использования информации WMI.

По сути, чтобы ответить на повторяющийся вопрос: Получение основной и дополнительной версий ОС, а также версий сборки для Windows 8.1 и более поздних версий в Delphi 2007.

Начиная с W2K, вы можете использовать NetServerGetInfo.NetServerGetInfo возвращает правильную информацию о W7 и W8.1, невозможно протестировать на 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;

Одно замечание по поводу использования NetServerGetInfo(), который все еще работает в Windows 10 (10240.th1_st1)...

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

sv101_version_major

Основной номер версии и тип сервера.

Основной номер версии выпуска операционной системы указан в наименьших значительных 4 битах.Тип сервера указан в наиболее значимых 4 битах.Приложение, определяемое в заголовке LMServer.H, должна использоваться приложением для получения основного номера версии от этого члена, определяется в BitMask, определенный в заголовке lmserver.h.

Другими словами, (sv101_version_major и MAJOR_VERSION_MASK).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top