Как определить истинную версию Windows?

36

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

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

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

  • 1
    Каждый, кто пришел к этому вопросу, должен также прочитать эту статью. Проверка версии (просто не делайте этого) Проверка версии затруднительна. Прежде чем копировать код из ответов на этот вопрос, убедитесь, что вам абсолютно необходимо это сделать.
  • 1
    Хороший вопрос, я также хотел бы знать это, а также вернуть дополнительную информацию, такую как Service Pack, Home / Professional / Ultimate Edition и т. д.
Показать ещё 2 комментария
Теги:
winapi
version

9 ответов

28
Лучший ответ

Лучший подход, который я знаю, - проверить, экспортирован ли определенный 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 был извлечен из бесплатной библиотеки DSiWin32):

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 }

--- обновлено 2009-10-09

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

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

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

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

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

  • 0
    Я так хочу отредактировать этот пост, чтобы выровнять версии в первом разделе, но мне не хватает репутации.
  • 0
    Я бы тоже, но таблицы в настоящее время не отображаются правильно :(
Показать ещё 12 комментариев
24

Запрос WMI:

"Select * from Win32_OperatingSystem"

EDIT: На самом деле лучше было бы:

"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;
  • 0
    Элегантный! Мне это нравится - и это никогда не приходило мне в голову.
  • 0
    Одна проблема с WMI - она появилась только в Windows 2000. Если вы знаете, что ваш код не будет работать в 9x или NT 3/4, тогда подход WMI подойдет.
Показать ещё 3 комментария
10

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

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

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

  • 0
    Таков подход, который рекомендует MSDN.
7

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

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

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

или другие клавиши из

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
  • 1
    За исключением платформы 9x, конечно.
  • 0
    Теперь это элегантно. Я рад, что продолжал читать, а не начинать использовать опцию GetVersionEx. Держите это простым и красивым.
Показать ещё 6 комментариев
4

хранилище реальной версии на блоке 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.

  • 0
    Этот ложный стандартный stdcall делает весь фрагмент очень подозрительным.
3

Следующие действия для меня в 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()", но я еще не пробовал его.

  • 1
    Мое приложение Delphi 7 работает на Windows 8.1. Вызов RtlGetVersion из ntdll.dll возвращает «5.1» (номера версий для Windows XP). Это то же самое, что числа, возвращаемые из GetVersionEx . Еще не пробовал функцию Netapi32 .
  • 0
    @remylebeau - Какой из этих 3 методов вы считаете более надежным для будущей совместимости (будущие версии Windows)? RtlGetVersion?
Показать ещё 2 комментария
1

Одна заметка об использовании 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 бита. Битовая маска MAJOR_VERSION_MASK, определенная в Заголовок Lmserver.h {0x0F} должен использоваться приложением для получения основной номер версии этого участника.

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

  • 0
    Хороший улов, спасибо
1

По существу, чтобы ответить на дубликат Q: Получение основных, второстепенных версий и версий ОС для ОС 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;
  • 0
    Возможно, было бы более полезно подождать, пока вы не сможете протестировать Win10, поскольку эта тема активно обсуждается в этой более свежей версии q: stackoverflow.com/questions/31753092
  • 0
    Я тестировал этот код в Windows 10 Preview (у меня еще нет релизной версии). Без идентификатора GUID для Windows 10 в манифесте NetServerGetInfo() (а также RtlGetVersion() в ntdll.dll) сообщает о версии как 10.0, тогда как GetVersionEx() сообщает о версии как 6.2, как GetVersionEx() в документации.
Показать ещё 1 комментарий
0

Примечание. Габр спрашивает о подходе, который может обойти ограничения 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-битные окна 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.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню