Думаю каждый читатель хотя бы раз сталкивался с ситуацией, когда на современной ОС не удавалось запустить старую программу, и помогал в этом случае режим совместимости Windows.
В основе работы данного механизма лежит перехват различных функций и эмуляция их поведения, свойственного указанной версии Windows, например, эмулируются ключи реестра, каталоги с документами и прочее. Все это нужно для того, чтобы программа думала, что запущена в выбранной среде.
Если приложение запущено в режиме совместимости, то вызов GetVersionEx вернет фиктивную версию Windows, что, вероятно, не подойдет для системных программ типа твикеров ОС. Как быть в этом случае?
Метод работает, более того, он полностью документирован, но медленный, и требует тянуть в проект кучу кода по работе с WMI.
Вполне пригодный способ, но лично мне по непонятным причинам он не нравится, благо есть еще альтернатива.
----
Виктор Вик Федоренков
В основе работы данного механизма лежит перехват различных функций и эмуляция их поведения, свойственного указанной версии 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, можно увидеть, что это значение не эмулируется:
Метод работает, более того, он полностью документирован, но медленный, и требует тянуть в проект кучу кода по работе с 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 это так:Вполне пригодный способ, но лично мне по непонятным причинам он не нравится, благо есть еще альтернатива.
Анализируем 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 она уже считается документированной :)
----
Виктор Вик Федоренков
Комментариев нет:
Отправить комментарий