How to detect true Windows version?
The best approach I know is to check if specific API is exported from some DLL. Each new Windows version adds new functions and by checking the existance of those functions one can tell which OS the application is running on. For example, Vista exports GetLocaleInfoEx from kernel32.dll while previous Windowses didn't.
To cut the long story short, here is one such list containing only exports from kernel32.dll.
> *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
Writing the function to determine the real OS version is simple; just proceed from newest OS to oldest and use GetProcAddress to check exported APIs. Implementing this in any language should be trivial.
The following code in Delphi was extracted from the free DSiWin32 library):
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.dwPlatformIDend; { 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 }
--- updated 2009-10-09
It turns out that it gets very hard to do an "undocumented" OS detection on Vista SP1 and higher. A look at the API changes shows that all Windows 2008 functions are also implemented in Vista SP1 and that all Windows 7 functions are also implemented in Windows 2008 R2. Too bad :(
--- end of update
FWIW, this is a problem I encountered in practice. We (the company I work for) have a program that was not really Vista-ready when Vista was released (and some weeks after that ...). It was not working under the compatibility layer either. (Some DirectX problems. Don't ask.)
We didn't want too-smart-for-their-own-good users to run this app on Vista at all - compatibility mode or not - so I had to find a solution (a guy smarter than me pointed me into right direction; the stuff above is not my brainchild). Now I'm posting it for your pleasure and to help all poor souls that will have to solve this problem in the future. Google, please index this article!
If you have a better solution (or an upgrade and/or fix for mine), please post an answer here ...
WMI QUery:
"Select * from Win32_OperatingSystem"
EDIT: Actually better would be:
"Select Version from Win32_OperatingSystem"
You could implement this in Delphi like so:
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;