How to detect true Windows version?
Asked Answered
I

10

43

I know I can call the GetVersionEx Win32 API function to retrieve the Windows version. In most cases, the 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 behavior and get the true Windows version?

Immortelle answered 11/9, 2008 at 17:21 Comment(6)
Everyone who has arrived at this question should also read this article. Version Checking (Just Don’t Do It) Version checking is hard to get right. Make sure you absolutely need to do it in the first place before copy pasting code from answers to this SO question.Brattishing
good question, I would also like to know this and also return additional information such as Service Pack, Home/Professional/Ultimate edition etc too.Ahrendt
Craig; Check my JCL answer out. It doesn't bypass the compatibility layer, but it DOES tell you the truth if Windows hasn't been configured to lie to you, and it can tell you about editions, and everything.Fennelly
archive.org link for the link given by scobi: web.archive.org/web/20121013161123/http://windowsteamblog.com/…Enschede
techthoughts.info/windows-version-numbersBroucek
This is pretty much a duplicate of Detecting Windows 10 version.Nu
I
28

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

Immortelle answered 11/9, 2008 at 17:25 Comment(10)
this should be pretty easy to translate to C#.Fennelly
Not sure if that table can be trusted, AFAIK Beep exists on NT4 and ConnectNamedPipe on 9xKass
The JEDI JCL can determine Server versions as well.Fennelly
@WarrenP, which JCL function would that be?Immortelle
There's actually a lot of cool Windows-Version-check stuff in JCL, so I created an answer for this question.Fennelly
JCL just calls GetVersionEx and is therefore tricked by the compatibility layer.Immortelle
Oh, that's important to note. I'll see about logging a bug in JCL and adding a GetRealWindowsVersion function. In which case, we'd need to use an API check approach like yours, to defeating the compatibility layer.Fennelly
Note Windows service packs often add back-ported dll exports from later versions.Soilure
Besides being an overley complex method, this isn't really going to help with future versions of WindowsChrissie
Here is list you can use to support later OS (Win 7 etc.) geoffchappell.com/studies/windows/win32/kernel32/apiModel
M
26

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;
Minhminho answered 11/9, 2008 at 17:24 Comment(4)
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.Immortelle
Does anyone still run 9x or NT?Nazario
Is WMI weird or what? "Select" doesn't work on my WMI, but "path Win32_OperatingSystem" did work. Is WMI a crazy piece of under-documented wonkiness, or what?Fennelly
So you are accessing wmi through the wmic console app it sounds like. When I say WMI query I am talking about accessing it via a method that supports the WQL query language (I realize that is redundant) which WMIC does not. So to answer your question, some portions of WMI are not particularly well documented because any software vendor can create classes in WMI pretty much at will much like the registry, but the portions that are created by MS and especially the portions dealing with the OS are actually quite well documented.Minhminho
P
11

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)"

Pyrotechnic answered 11/9, 2008 at 17:37 Comment(1)
This is the approach that MSDN recommends.Blasting
P
8

Another solution:

read the following registry entry:

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

or other keys from

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
Pyrotechnic answered 11/9, 2008 at 18:49 Comment(8)
Now, that is elegant. I'm glad I kept reading rather than begin using the GetVersionEx option. Keep it simple and beautiful.Chowchow
Reading strings from the registry and parsing them is not a good idea unless it is specifically documented by Microsoft. Are you certain the ProductName is not localized to different languages? Are you sure you got every variant of the product name? The above advice is the exact kind of thing that makes the App Compat team over at Microsoft pull their hair out. -1Brattishing
Well, then they should provide an API/an official solution to get this information. Hiding information is rarely a good thing. As you can see, every solution is only a work-around.Pyrotechnic
This is certainly not elegantSedentary
When I read CurrentVersion on my Windows 8.1 laptop, astonishingly it comes back with "5.1" (the version numbers for Windows XP)! This was using an older Delphi 7 application without a manifest specifically targeting Windows 8.1. Using regedit, the value is definitely "6.3". This suggests Windows must be deliberately intercepting the read and applying some sort of compatibility shim. The only method I found to be reliable is to read the version numbers of a core system dll (I chose kernel32.dll) as per the other suggestion from botismarius. Seems a bit "hacky" but it works.Nightshade
Continuing from above, ProductName returns "Microsoft Windows XP" when the actual entry is "Windows 8.1"!Nightshade
@Nightshade seems that Microsoft is taking the compatibility to the extreme pole.Pyrotechnic
If you must point fingers, point them at yourself. The registry is not a programming interface. The issue is in your proposed solution, not in Microsoft changing implementation details.Nu
M
6

real version store on PEB block of process information.

Sample for Win32 app (Delphi Code)

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.

Meade answered 21/6, 2014 at 20:16 Comment(1)
That spurious stdcall makes the whole snippet highly suspicious.Stream
B
6

The following works for me in Windows 10 without the Windows 10 GUID listed in the application manifest:

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.

Update: NetWkstaGetInfo() would probably also work, similar to 'NetServerGetInfo()`, but I have not try it yet.

Blasting answered 31/7, 2015 at 23:37 Comment(4)
My Delphi 7 application is running on Windows 8.1. Invoking RtlGetVersion from ntdll.dll returns "5.1" (the version numbers for Windows XP). This is the same as the numbers returned from GetVersionEx. Haven't tried the Netapi32 function yet.Nightshade
@remylebeau - Which of these 3 methods you think is more reliable for future compatibility (future versions of Windows)? RtlGetVersion?Broucek
@Nightshade works fine for me and everyone else who uses it (numerous posts on SO showing that). Do you, by chance, have your EXE running in XP Compatibility mode? Without Compatibility Mode, and lacking a suitable manifest, GetVersionEx would be reporting the version number of Windows 8, not XP.Blasting
@RemyLebeau: Yes, well spotted! When I disabled XP Compatibility Mode, the operating system version returns as Major=6 and Minor=2 (ie. Windows 8). Have since tried this on Windows 10 with the same result.Nightshade
F
1

Note: Gabr is asking about an approach that can bypass the limitations of GetVersionEx. JCL code uses GetVersionEx, and is thus subject to compatibility layer. This information is for people who don't need to bypass the compatibility layer, only.

Using the Jedi JCL, you can add unit JclSysInfo, and call function GetWindowsVersion. It returns an enumerated type TWindowsVersion.

Currently JCL contains all shipped windows versions, and gets changed each time Microsoft ships a new version of Windows in a box:

  TWindowsVersion =
   (wvUnknown, wvWin95, wvWin95OSR2, wvWin98, wvWin98SE, wvWinME,
    wvWinNT31, wvWinNT35, wvWinNT351, wvWinNT4, wvWin2000, wvWinXP,
    wvWin2003, wvWinXP64, wvWin2003R2, wvWinVista, wvWinServer2008,
    wvWin7, wvWinServer2008R2);

If you want to know if you're running 64-bit windows 7 instead of 32-bit, then call JclSysInfo.IsWindows64.

Note that JCL allso handles Editions, like Pro, Ultimate, etc. For that call GetWindowsEdition, and it returns one of these:

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);

For historical interest, you can check the NT-level edition too with the NtProductType function, it returns:

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

Note that "N editions" are detected above. That's an EU (Europe) version of Windows, created due to EU anti-trust regulations. That's a pretty fine gradation of detection inside the JCL.

Here's a sample function that will help you detect Vista, and do something special when on Vista.

function IsSupported:Boolean;
begin
  case GetWindowsVersion of
     wvVista:  result := false; 
    else
      result := true;
  end;
end;

Note that if you want to do "greater than" checking, then you should just use other techniques. Also note that version checking can often be a source of future breakage. I have usually chosen to warn users and continue, so that my binary code doesn't become the actual source of breakage in the future.

Recently I tried to install an app, and the installer checked my drive free space, and would not install, because I had more than 2 gigabytes of free space. The 32 bit integer signed value in the installer became negative, breaking the installer. I had to install it into a VM to get it to work. Adding "smart code" often makes your app "stupider". Be wary.

Incidentally, I found that from the command line, you can run WMIC.exe, and type path Win32_OperatingSystem (The "Select * from Win32_OperatingSystem" didn't work for me). In future perhaps JCL could be extended to use the WMI information.

Fennelly answered 14/11, 2011 at 15:33 Comment(0)
D
1

Essentially to answer duplicate Q: Getting OS major, minor, and build versions for Windows 8.1 and up in Delphi 2007

Starting with W2K you can use NetServerGetInfo. NetServerGetInfo returns the correct info on W7 and W8.1, unable to test on 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;
Digitize answered 31/7, 2015 at 21:32 Comment(3)
Might be more useful to wait until you can test against Win10, seeing as the topic has been under active discussion in this more recent q: stackoverflow.com/questions/31753092Amarelle
I tested this code On Windows 10 Preview (I don't have the release version yet). Without a Windows 10 GUID in the manifest, NetServerGetInfo() (and also RtlGetVersion() in ntdll.dll) reports the version as 10.0, whereas GetVersionEx() reports the version as 6.2 as documented.Blasting
Good to know, I need to wait until the "Something Happened" error is resolved :)Digitize
S
1

One note about using NetServerGetInfo(), which does work still on Windows 10 (10240.th1_st1)...

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

sv101_version_major

The major version number and the server type.

The major release version number of the operating system is specified in the least significant 4 bits. The server type is specified in the most significant 4 bits. The MAJOR_VERSION_MASK bitmask defined in the Lmserver.h header {0x0F} should be used by an application to obtain the major version number from this member.

In other words, (sv101_version_major & MAJOR_VERSION_MASK).

Stitt answered 1/8, 2015 at 18:31 Comment(0)
B
-1

There are multiple ways to get the Windows version in Delphi, but none of them are always 100% accurate. But here are the different methods and how accurate they are.

Using TOSVersion

This method is currently the least accurate when run under compatibility mode, but it is the easiest to implement as it is a native function built into Delphi in the System.SysUtils unit.

I would also recommend this even though it fails when an app is run under compatibility mode because TOSVersion is cross-platform (VCL/FMX) and very easy to use and understand.

uses System.SysUtils;

...

function GetWindowsVersion(): String;
begin
  Result := TOSVersion.Name;
end;

You can also do TOSVersion.ToString to get the full Windows Version Details such as "Windows 11 (Version 23H2, OS Build 25977.1000, 64-bit Edition)" instead of just "Windows 11".

Using ProductName from Windows Registry

This version is more accurate, because it checks the Windows Version name straight from the Windows Registry, but even this sometimes fails as Windows actually changes the registry reading value when the app is run in compatibility mode.

uses System.SysUtils, Winapi.Windows, System.Win.Registry;

...

function GetWindowsVersion(): String;
begin
  Result := TOSVersion.Name;
  var Registry := TRegistry.Create(KEY_READ);
  try
    Registry.RootKey := HKEY_LOCAL_MACHINE;
    if Registry.OpenKey('SOFTWARE\Microsoft\Windows NT\CurrentVersion', False) then
    try
      Result := Registry.ReadString('ProductName');
    finally
      Registry.CloseKey;
    end;
  finally
    Registry.Free;
  end;
end;

Using Build Number from Windows Registry

This method is one of the most accurate methods, but could also fail and report the wrong build number, especially for very old versions of Windows.

uses System.SysUtils, Winapi.Windows, System.Win.Registry;

...

function GetWindowsVersion(): String;
begin
  Result := TOSVersion.Name;
  var Registry := TRegistry.Create(KEY_READ);
  try
    Registry.RootKey := HKEY_LOCAL_MACHINE;
    if Registry.OpenKey('SOFTWARE\Microsoft\Windows NT\CurrentVersion', False) then
    try
      Result := Registry.ReadString('ProductName');
      if (Result.ToLower.Contains('server') = False) then
      begin
        var vBuild := Registry.ReadString('CurrentBuild').ToInteger;
        if (vBuild >= 103) then Result := 'Windows 3.1';
        if (vBuild >= 153) then Result := 'Windows 3.2';
        if (vBuild >= 528) then Result := 'Windows NT 3.1';
        if (vBuild >= 807) then Result := 'Windows NT 3.5';
        if (vBuild >= 950) then Result := 'Windows 95';
        if (vBuild >= 1381) then Result := 'Windows NT 4.0';
        if (vBuild >= 1998) then Result := 'Windows 98';
        if (vBuild >= 2195) then Result := 'Windows 2000';
        if (vBuild >= 2600) then Result := 'Windows XP';
        if (vBuild >= 6002) then Result := 'Windows Vista';
        if (vBuild >= 7601) then Result := 'Windows 7';
        if (vBuild >= 9200) then Result := 'Windows 8';
        if (vBuild >= 9600) then Result := 'Windows 8.1';
        if (vBuild >= 10240) then Result := 'Windows 10';
        if (vBuild >= 22000) then Result := 'Windows 11';
      end;
    finally
      Registry.CloseKey;
    end;
  finally
    Registry.Free;
  end;
end;

Using Build Number from explorer.exe

This method works even when run under compatibility mode for normal Windows Desktop and is pretty accurate, but it might still fail under compatibility mode when run on Windows Server.

uses System.SysUtils;

...

function GetWindowsVersion(): String;
begin
  var vMajor: Cardinal := 0;
  var vMinor: Cardinal := 0;
  var vBuild: Cardinal := 0;
  Result := TOSVersion.Name;

  var ExplorerPath := 'C:\WINDOWS\explorer.exe';
  if (Result.ToLower.Contains('server') = False) and (TFile.Exists(ExplorerPath)) then
  begin
    GetProductVersion(ExplorerPath, vMajor, vMinor, vBuild);

    if (vBuild >= 103) then Result := 'Windows 3.1';
    if (vBuild >= 153) then Result := 'Windows 3.2';
    if (vBuild >= 528) then Result := 'Windows NT 3.1';
    if (vBuild >= 807) then Result := 'Windows NT 3.5';
    if (vBuild >= 950) then Result := 'Windows 95';
    if (vBuild >= 1381) then Result := 'Windows NT 4.0';
    if (vBuild >= 1998) then Result := 'Windows 98';
    if (vBuild >= 2195) then Result := 'Windows 2000';
    if (vBuild >= 2600) then Result := 'Windows XP';
    if (vBuild >= 6002) then Result := 'Windows Vista';
    if (vBuild >= 7601) then Result := 'Windows 7';
    if (vBuild >= 9200) then Result := 'Windows 8';
    if (vBuild >= 9600) then Result := 'Windows 8.1';
    if (vBuild >= 10240) then Result := 'Windows 10';
    if (vBuild >= 22000) then Result := 'Windows 11';
  end;
end;
Basketball answered 10/2 at 12:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.