Windows の真のバージョンを検出するにはどうすればよいですか?
質問
GetVersionEx Win32 API 関数を呼び出して Windows バージョンを取得できることはわかっています。ほとんどの場合、戻り値は Windows のバージョンを反映しますが、そうでない場合もあります。
ユーザーが互換性レイヤーの下でアプリケーションを実行した場合、GetVersionEx は実際のバージョンではなく、互換性レイヤーによって強制されたバージョンを報告します。たとえば、Vista を実行していて、「Windows NT 4」互換モードでプログラムを実行すると、GetVersionEx はバージョン 6.0 ではなく 4.0 を返します。
この動作を回避して真の Windows バージョンを取得する方法はありますか?
解決
私が知っている最良のアプローチは、特定の API が何らかの DLL からエクスポートされているかどうかを確認することです。Windows の新しいバージョンごとに新しい機能が追加され、それらの機能の存在を確認することで、アプリケーションがどの OS で実行されているかがわかります。たとえば、Vista のエクスポート GetLocaleInfoEx 以前の Windows では行われませんでしたが、kernel32.dll から。
話を手短にまとめると、kernel32.dll からのエクスポートのみを含むリストの 1 つを次に示します。
> *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
実際の 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さん、この記事をインデックスしてください!
より良い解決策 (または私の場合のアップグレードや修正) がある場合は、ここに回答を投稿してください。
他のヒント
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;
システムファイルのバージョンを取得してみるのはどうでしょうか?
最適なファイルは、%WINDIR%\System32\kernel32.dll にある kernel32.dll です。
ファイルのバージョンを取得するための API があります。例えば:Windows XPを使用しています -> 「5.1.2600.5512 (xpsp.080413-2111)」
別の解決策:
次のレジストリ エントリを読み取ります。
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductName
または他のキー
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
実際のバージョンはプロセス情報の 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.
アプリケーション マニフェストに 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()」と同様に機能するでしょうが、私はまだ試していません。
注記: Gabr 氏は、次のような制限を回避できるアプローチについて尋ねています。 GetVersionEx
. 。JCL コードは GetVersionEx を使用するため、互換性レイヤーの影響を受けます。この情報は、互換性レイヤーをバイパスする必要がない人のみを対象としています。
Jedi JCL を使用すると、ユニット 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);
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 の独占禁止法に基づいて作成された Windows の EU (ヨーロッパ) バージョンです。これは、JCL 内での非常に細かい段階の検出です。
ここでは、Vista を検出し、Vista 上で特別な処理を実行するのに役立つサンプル関数を示します。
function IsSupported:Boolean;
begin
case GetWindowsVersion of
wvVista: result := false;
else
result := true;
end;
end;
「より大きい」チェックを行いたい場合は、他の手法を使用する必要があることに注意してください。また、バージョン チェックが将来の破損の原因となる可能性があることにも注意してください。私は通常、バイナリ コードが将来実際に破損の原因にならないように、ユーザーに警告して続行することを選択しています。
最近、アプリをインストールしようとしましたが、インストーラーがドライブの空き容量をチェックしましたが、空き容量が 2 GB を超えていたため、インストールできませんでした。インストーラー内の 32 ビット整数の符号付き値が負になり、インストーラーが中断されました。動作させるには、VM にインストールする必要がありました。「スマート コード」を追加すると、多くの場合、アプリが「愚か」になります。注意してください。
ちなみに、コマンド ラインから WMIC.exe を実行し、次のように入力できることがわかりました。 path Win32_OperatingSystem
(「Select * from Win32_OperatingSystem」は私にとっては機能しませんでした)。将来的には、WMI 情報を使用できるように JCL が拡張される可能性があります。
基本的に重複したQに答えるには: Delphi 2007 で Windows 8.1 以降の OS メジャー、マイナー、ビルド バージョンを取得する
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;
NetServerGetInfo() の使用に関する注意点が 1 つあります。これは Windows 10 (10240.th1_st1) でも動作します...
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370903%28v=vs.85%29.aspx
sv101_バージョン_メジャー
メジャー バージョン番号とサーバーの種類。
オペレーティングシステムの主要なリリースバージョン番号は、最も重要な4ビットで指定されています。サーバータイプは、最も重要な4ビットで指定されています。lmserver.hヘッダー{0x0f}で定義されているmajor_version_maskビットマスクは、このメンバーからメジャーバージョン番号を取得するためにアプリケーションで使用する必要があります。
つまり、(sv101_version_major & MAJOR_VERSION_MASK)。