互換モードからWindowsの実際のバージョンを確認する方法

誰もが少なくとも一度は最新のOSでは古いプログラムを実行できなかった状況に直面したと思いますが、この場合はWindows互換モードが役に立ちました。







このメカニズムの操作は、さまざまな機能のインターセプトと、指定されたバージョンのWindowsに特有の動作のエミュレーションに基づいています。たとえば、レジ​​ストリキー、ドキュメントのあるディレクトリなどがエミュレートされます。 これらすべては、プログラムが選択された環境で実行されていると考えるために必要です。



アプリケーションが互換モードで実行されている場合、GetVersionExを呼び出すと、Windowsのダミーバージョンが返されますが、これはおそらくOS調整などのシステムプログラムには適していません。 この場合の対処方法



エクスポートされた関数の分析



ネットワークの広大さの中で、システムライブラリにエクスポートされた関数の有無を検出する方法に出会いました。 コード例:

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管理ツールキットです。 さらに、WMIは、Windowsプラットフォームを実行しているコンピューターインフラストラクチャのさまざまな部分の動作を集中管理および監視するための基本的な技術の1つです。



WMIからWindowsのバージョンを取得することもできます。 ドキュメントから、これはそのようなリクエストで実行できることがわかります:



SELECT Version FROM Win32_OperatingSystem







Windows XPとの互換モードでWMI Explorerを起動すると、この値がエミュレートされていないことがわかります。





さらに、このメソッドは機能しますが、完全に文書化されていますが、時間がかかり、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で文書化されていると考えられます。



結論



結論は単純です。実際のバージョンを本当に取得する必要がある場合はWMIが最適ですが、軽量なソリューションが必要な場合はPEBをご覧ください。



All Articles