tags:

views:

2831

answers:

6

I know I can call the GetVersionEx Win32 API function to retrieve Windows version. In most cases returned value reflects the version of my Windows, but sometimes that is not so.

If a user runs my application under the compatibility layer, then GetVersionEx won't be reporting the real version but the version enforced by the compatibility layer. For example, if I'm running Vista and execute my program in "Windows NT 4" compatibility mode, GetVersionEx won't return version 6.0 but 4.0.

Is there a way to bypass this behaviour and get true Windows version?

+16  A: 

WMI QUery:

"Select * from Win32_OperatingSystem"

EDIT: Actually better would be:

"Select Version from Win32_OperatingSystem"
EBGreen
Elegant! I like it - and it never crossed my mind.
gabr
One problem with WMI - it was only introduced in Windows 2000. If you know your code won't be running on 9x or NT 3/4 then the WMI approach is fine.
gabr
Does anyone still run 9x or NT?
Camilo Martin
+16  A: 

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.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 }

--- 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 ...

gabr
I so want to edit this post to align the versions in the first section, but I don't have enough reputation.
Brad Gilbert
I would too, but tables currently don't display correctly :(
gabr
A: 

How about testing a system file that exists only in some Windows version? It is a known fact that from a version to another some file appear or disappear.

botismarius
Unlikely to work reliably, the main difference between Win95A and Win95B is Winsock.dll, which can easily be copied to Win95A.
Brad Gilbert
+3  A: 

How about obtaining the version of a system file?

The best file would be kernel32.dll, located in %WINDIR%\System32\kernel32.dll.

There are APIs to obtain the file version. eg: I'm using Windows XP -> "5.1.2600.5512 (xpsp.080413-2111)"

botismarius
+5  A: 

Another solution:

read the following registry entry:

HLKM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersion\Product Name

or other keys from

HLKM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersion
botismarius
Except on 9x platform, of course.
gabr
Now, that is elegant. I'm glad I kept reading rather than begin using the GetVersionEx option. Keep it simple and beautiful.
Sam
A: 

It would also be nice if you could determine if you are running under Wine or ReactOS, or some other technology that presents itself as a Windows environment.

Brad Gilbert
Agree. If you know a way, post it here ...
gabr
There is indeed a way - in the ReactOS newsletter it was once mentioned that some ReactOS users could actually connect to Windows Update (and "update" their systems) until MS found a way to discriminate both WiNE and ReactOS. It must be somewhere in the archives, they explained how MS was doing it.
Joe Pineda