[Из песочницы] Как узнать реальную версию Windows из режима совместимости

Думаю каждый хотя бы раз сталкивался с ситуацией, когда на современной ОС не удавалось запустить старую программу, и помогал в этом случае режим совместимости Windows.e5d9b2681959c92ed6d83a9b7451bc1a.png

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

Если приложение запущено в режиме совместимости, то вызов GetVersionEx вернет фиктивную версию Windows, что, вероятно, не подойдет для системных программ типа твикеров ОС. Как быть в этом случае?

Анализ экспортируемых функцийНа просторах сети наткнулся на способ детектирования по наличию/отсутствию экспортируемых функций у системных библиотек. Пример кода: TDSiWindowsVersion = (wvUnknown, wvWin31, wvWin95, wvWin95OSR2, wvWin98, wvWin98SE, wvWinME, wvWin9x, wvWinNT3, wvWinNT4, wvWin2000, wvWinXP, wvWinNT, wvWinServer2003, wvWinVista); 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 } Решение интересное, но не считаю его приемлемым, так как с выходом каждой версии Windows требуется нетривиальная поддержка.

Используя WMI Из википедии Windows Management Instrumentation (WMI) в дословном переводе — это инструментарий управления Windows. Если говорить более развернутo, то WMI — это одна из базовых технологий для централизованного управления и слежения за работой различных частей компьютерной инфраструктуры под управлением платформы Windows.

Из WMI можно получить и версию Windows. Из документации следует что это можно сделать таким запросом: SELECT Version FROM Win32_OperatingSystem

Запустив WMI Explorer в режиме совместимости с Windows XP, можно увидеть, что это значение не эмулируется: e839da865c156eb1ddff260b624b8c3e.png

Метод работает, более того, он полностью документирован, но медленный, и требует тянуть в проект кучу кода по работе с WMI.

Ищем в реестре Пожалуй самый элегантный и правильный способ найденный в сети — это подсмотреть значение в реестре: HLKM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersionНу что же, попробуем: with TRegistry.Create do try RootKey:= HKEY_LOCAL_MACHINE; if OpenKeyReadOnly ('SOFTWARE\Microsoft\Windows NT\CurrentVersion') then Edit1.Text:= ReadString ('CurrentVersion'); finally Free; end; К сожалению, проверив его на Windows 7 оказалось, что этот ключ реестра эмулируется. Похоже в предыдущих версиях Windows этот способ работал, но, увы — сейчас этот трюк не сработает.Анализ версии kernel32.dll Сам не проверял, но говорят, что версия файла у kernel32.dll совпадает с версией Windows. На моем компьютере с Windows 7 это так: 0c487c6c00bc33108a4b6451a1f3bd45.pngВполне пригодный способ, но лично мне по непонятным причинам он не нравится, благо есть еще альтернатива.Анализируем PEB процесса У каждого Windows-процесса есть структура описывающая его, называется она PEB. Она заполняется при старте процесса и содержит в себе адрес загрузки, список загруженных модулей, параметры командной строки, и, в том числе, версию Windows. Ниже пример модуля, используя который можно получить реальную версию Windows (тестировался на Delphi 2010 Win32): unit RealWindowsVerUnit; interface uses Windows; var //Реальная версия ОС, а не та что выдается системой при запуске //в режиме совместимости 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; //Получить блок PEB своего процесса function GetPDB: PPEB; stdcall; asm MOV EAX, DWORD PTR FS:[30h] end; initialization //Получаем реальную версию ОС Win32MajorVersionReal:= GetPDB^.OSMajorVersion; Win32MinorVersionReal:= GetPDB^.OSMinorVersion; end. Скорость работы моментальная, ничего лишнего, единственное НО — недокументированная структура PEB, но как известно Microsoft очень заботится об обратной совместимости, так что с большой долей оптимизма можно считать, что раз описание структуры давно бродит по интернету, то в Microsoft она уже считается документированной.

Выводы А выводы простые, если очень нужно получить реальную версию то WMI отличный вариант, если же требуется легковесное решение — смотри в PEB.

© Habrahabr.ru