40

GetVersionEx Win32 API 関数を呼び出して、Windows のバージョンを取得できることはわかっています。ほとんどの場合、返される値は Windows のバージョンを反映していますが、そうでない場合もあります。

ユーザーが互換性レイヤーの下でアプリケーションを実行すると、GetVersionEx は実際のバージョンではなく、互換性レイヤーによって強制されたバージョンを報告します。たとえば、Vista を実行していて、プログラムを「Windows NT 4」互換モードで実行すると、GetVersionEx はバージョン 6.0 ではなく 4.0 を返します。

この動作をバイパスして真の Windows バージョンを取得する方法はありますか?

4

9 に答える 9

29

私が知っている最善の方法は、特定の API が何らかの DLL からエクスポートされているかどうかを確認することです。新しい Windows バージョンごとに新しい機能が追加され、それらの機能の存在を確認することで、アプリケーションが実行されている OS を知ることができます。たとえば、Vistaは kernel32.dll からGetLocaleInfoExをエクスポートしますが、以前の Windows はそうではありませんでした。

簡単に言うと、kernel32.dll からのエクスポートのみを含むリストの 1 つを次に示します。

> *関数: で実装*  
> GetLocaleInfoEx: Vista  
> GetLargePageMinimum: Vista、Server 2003  
GetDLLDirectory: Vista、Server 2003、XP SP1  
GetNativeSystemInfo: Vista、Server 2003、XP SP1、XP  
ファイルの置換: 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  
ビープ音: Vista、Server 2003、XP SP1、XP、2000、ME、98、95 OSR2、95  

実際の OS バージョンを判別する関数を作成するのは簡単です。最新の OS から最も古い OS に進み、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 以降で「文書化されていない」OS 検出を行うのは非常に困難であることが判明しました。API の変更を見ると、Windows 2008 のすべての機能が Vista SP1 にも実装されており、Windows 7 のすべての機能が Windows 2008 R2 にも実装されていることがわかります。残念な :(

--- 更新終了

FWIW、これは私が実際に遭遇した問題です。私たち (私が勤務している会社) には、Vista がリリースされたとき (そしてその数週間後...) には実際には Vista 対応ではなかったプログラムがあります。互換性レイヤーの下でも機能していませんでした。(いくつかの DirectX の問題。質問しないでください。)

互換モードであろうとなかろうと、賢すぎるユーザーにこのアプリを Vista で実行してほしくないので、解決策を見つけなければなりませんでした (私よりも賢い人が私に正しい方向を教えてくれました。上記のものは私の発案によるものではありません)。今、私はあなたの喜びのために、そして将来この問題を解決しなければならないすべての貧しい魂を助けるためにそれを投稿しています. Google、この記事をインデックスしてください!

より良い解決策(または私のアップグレードや修正)がある場合は、ここに回答を投稿してください...

于 2008-09-11T17:25:30.597 に答える
26

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;
于 2008-09-11T17:24:24.717 に答える
11

システムファイルのバージョンを取得するのはどうですか?

最適なファイルは、%WINDIR%\System32\kernel32.dll にある kernel32.dll です。

ファイルのバージョンを取得するための API があります。例: Windows XP を使用しています -> "5.1.2600.5512 (xpsp.080413-2111)"

于 2008-09-11T17:37:18.803 に答える
8

別の解決策:

次のレジストリ エントリを読み取ります。

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

または他のキー

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
于 2008-09-11T18:49:59.550 に答える
6

プロセス情報の 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.

于 2014-06-21T20:16:43.823 に答える
1

注: Gabr は、の制限を回避できるアプローチについて質問していGetVersionExます。JCL コードは GetVersionEx を使用するため、互換性レイヤーの対象となります。この情報は、互換性レイヤーをバイパスする必要がない人のみを対象としています。

Jedi JCL を使用して、ユニット JclSysInfo を追加し、 function を呼び出すことができます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);

32 ビットではなく 64 ビットの Windows 7 を実行しているかどうかを知りたい場合は、 を呼び出します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);

歴史的な関心のために、NtProductType 関数を使用して NT レベルのエディションも確認できます。次の値が返されます。

 TNtProductType =       (ptUnknown, ptWorkStation, ptServer, ptAdvancedServer,        
        ptPersonal, ptProfessional, ptDatacenterServer, 
        ptEnterprise, ptWebEdition);

上記で「N エディション」が検出されることに注意してください。これは、EU の反トラスト規制のために作成された EU (ヨーロッパ) バージョンの 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("Select * from Win32_OperatingSystem" は機能しませんでした)。将来的には、WMI 情報を使用するように JCL が拡張される可能性があります。

于 2011-11-14T15:33:08.600 に答える
1

Windows 10 (10240.th1_st1) でも動作する NetServerGetInfo() の使用に関する 1 つの注意事項...

https://msdn.microsoft.com/en-us/library/windows/desktop/aa370903%28v=vs.85%29.aspx

sv101_version_major

メジャー バージョン番号とサーバーの種類。

オペレーティング システムのメジャー リリース バージョン番号は、最下位 4 ビットで指定されます。サーバーの種類は、上位 4 ビットで指定されます。Lmserver.h ヘッダー {0x0F} で定義されている MAJOR_VERSION_MASK ビットマスクは、アプリケーションがこのメンバーからメジャー バージョン番号を取得するために使用する必要があります。

つまり、(sv101_version_major & MAJOR_VERSION_MASK) です。

于 2015-08-01T18:31:54.313 に答える