Как определить истинную версию Windows? - PullRequest
36 голосов
/ 11 сентября 2008

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

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

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

Ответы [ 9 ]

29 голосов
/ 11 сентября 2008

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

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

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

26 голосов
/ 11 сентября 2008

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;
10 голосов
/ 11 сентября 2008

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

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

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

8 голосов
/ 11 сентября 2008

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

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

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

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

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
5 голосов
/ 22 июня 2014

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

Пример приложения Win32 (Delphi Code)

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.

4 голосов
/ 01 августа 2015

В 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 голос
/ 01 августа 2015

Одна заметка об использовании 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).

1 голос
/ 01 августа 2015

По существу, чтобы ответить на дубликаты 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;
1 голос
/ 14 ноября 2011

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

Используя JCL Jedi, вы можете добавить модуль 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 allso обрабатывает выпуски, такие как 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-разрядного целого со знаком в установщике стало отрицательным, что нарушило установщик. Я должен был установить это в VM, чтобы заставить это работать. Добавление «умного кода» часто делает ваше приложение «глупее». Будьте осторожны.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...