Freebie Friday: Windows version info
Coincidence or not, but 26 years ago, in 1995, not only Delphi 1 was released by Borland but Microsoft also released Windows 95 that from that moment on skyrocketed in popularity and went for world domination as operating system.
Who would have thought in 1995 that 26 years later, detecting on what Windows operating system your software is running would be more complex than ever?
Over the years, Microsoft released not only new major versions almost every 2 to 3 years but also all sorts of editions like the Windows NT operating system for example.
In 2015 Microsoft released Windows 10 and decided it would continue to release incremental updates all under the Windows 10 moniker but also here Microsoft went on to make it available in different editions (Education, Home, Pro, Enterprise) and at the same time continued to release major updates to its server operating system Windows 2016 Server and Windows 2019 Server.
Needless to say that proper Windows operating system version detection became non-trivial over the years. Added to this complexity is the fact that Microsoft decided to create a mechanism to return Windows version information to applications through its APIs different from the real Windows version and this depending on the application manifest. The reason for this approach was obviously for ensuring old applications would continue to work thinking they were running on older Windows operating systems, but it doesn’t make things easier.
So, forget about all this history, forget about all this complexity as after all, it is Friday today and we are heading to the weekend to celebrate the 26th anniversary of Delphi. This #FreebieFriday brings you one routine GetOperatingSystem() that returns the Windows version and also the version number as string:
procedure GetOperatingSystem(var AName: string; var AVersion: string); const SM_SERVERR2 = 89; VER_NT_WORKSTATION = $0000001; type pfnRtlGetVersion = function(var RTL_OSVERSIONINFOEXW): DWORD; stdcall; var osVerInfo: TOSVersionInfoEx; majorVer, minorVer, spmajorVer, spminorVer, buildVer, edition: Cardinal; ver: RTL_OSVERSIONINFOEXW; RtlGetVersion: pfnRtlGetVersion; procedure GetUnmanistedVersion(var majv,minv,buildv: cardinal); begin @RtlGetVersion := GetProcAddress(GetModuleHandle('ntdll.dll'), 'RtlGetVersion'); if Assigned(RtlGetVersion) then begin ZeroMemory(@ver, SizeOf(ver)); ver.dwOSVersionInfoSize := SizeOf(ver); if RtlGetVersion(ver) = 0 then begin majv := ver.dwMajorVersion; minv := ver.dwMinorVersion; buildv := ver.dwBuildNumber; end; end; end; function GetWindows10Edition: string; begin Result := 'Windows 10'; GetProductInfo(majorVer, minorVer, spmajorVer, spminorVer, edition); case edition and $FF of $62..$65: Result := 'Windows 10 Home'; $79..$7A: Result := 'Windows 10 Education'; $46,$04,$48,$1B,$54,$7D,$7E,$81,$82: Result := 'Windows 10 Enterprise'; $30,$31,$A1,$A2: Result := 'Windows 10 Pro'; end; end; begin AName := 'Unknown'; AVersion := '0'; // set operating system type flag osVerInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfoEx); if GetVersionEx(POSVersionInfo(@osVerInfo)^) then begin majorVer := osVerInfo.dwMajorVersion; minorVer := osVerInfo.dwMinorVersion; buildVer := osVerInfo.dwBuildNumber; AVersion := majorVer.ToString + '.' + minorVer.ToString + '.' + buildVer.ToString; case osVerInfo.dwPlatformId of VER_PLATFORM_WIN32_NT: // Windows NT/2000 begin if majorVer <= 4 then AName := 'Windows NT' else if (majorVer = 5) and (minorVer = 0) then AName := 'Windows 2000' else if (majorVer = 5) and (minorVer = 1) then AName := 'Windows XP' else if (majorVer = 5) and (minorVer = 2) then AName := 'Windows 2003' else if (majorVer = 6) and (minorVer = 0) then begin AName := 'Windows Vista'; if osVerInfo.wProductType = VER_NT_WORKSTATION then AName := 'Windows Vista' else AName := 'Windows Server 2008'; end else if (majorVer = 6) and (minorVer = 1) then begin if osVerInfo.wProductType = VER_NT_WORKSTATION then AName := 'Windows 7' else AName := 'Windows Server 2008R2'; end else if (majorVer = 6) and (minorVer = 2) then begin GetUnmanistedVersion(majorVer, minorVer, buildVer); AVersion := majorVer.ToString + '.' + minorVer.ToString + '.' + buildVer.ToString; if (majorVer = 6) and (minorVer = 2) then begin if osVerInfo.wProductType = VER_NT_WORKSTATION then AName := 'Windows 8' else AName := 'Windows Server 2012' end; if (majorVer = 6) and (minorVer = 3) then begin if osVerInfo.wProductType = VER_NT_WORKSTATION then AName := 'Windows 8.1' else AName := 'Windows Server 2012R2' end; if (majorVer = 10) and (minorVer = 0) then begin if osVerInfo.wProductType = VER_NT_WORKSTATION then AName := 'Windows 10' else begin if osVerInfo.dwBuildNumber >= 17763 then AName := 'Windows Server 2019' else AName := 'Windows Server 2016'; end; end; end else if (majorVer = 6) and (minorVer = 3) then begin if osVerInfo.wProductType = VER_NT_WORKSTATION then AName := 'Windows 8.1' else AName := 'Windows Server 2012R2' end else if (majorVer = 6) and (minorVer = 4) then begin if osVerInfo.wProductType = VER_NT_WORKSTATION then begin AName := GetWindows10Edition; end else begin if osVerInfo.dwBuildNumber >= 17763 then AName := 'Windows Server 2019' else AName := 'Windows Server 2016' end; end else if (majorVer = 10) and (minorVer = 0) then begin if osVerInfo.wProductType = VER_NT_WORKSTATION then AName := GetWindows10Edition else begin if osVerInfo.dwBuildNumber >= 17763 then AName := 'Windows Server 2019' else AName := 'Windows Server 2016' end; end else AName := 'Unknown'; end; VER_PLATFORM_WIN32_WINDOWS: // Windows 9x/ME begin if (majorVer = 4) and (minorVer = 0) then AName := 'Windows 95' else if (majorVer = 4) and (minorVer = 10) then begin if osVerInfo.szCSDVersion[1] = 'A' then AName := 'Windows 98 SE' else AName := 'Windows 98'; end else if (majorVer = 4) and (minorVer = 90) then AName := 'Windows ME' else AName := 'Unknown'; end; else AName := 'Unknown'; end; end; end;
Copy & paste this code and you can use it as:
var AName, AVersion: string; begin GetOperatingSystem(AName, AVersion); memo.Lines.Text := ANAme +' ' + AVersion; end;
Here, on this machine, it returns:
Windows 10 Pro 10.0.19041
EXEInfo.GetOperatingSystem: string;
TEXEInfo can in addition to this information extract detailed application version information from the running application or another application with EXEInfo.GetVersionInfoOfApp(EXEName);
Enjoy this #FreebieFriday and don’t hesitate to let us know what other interesting Freebies you wish to see or share an interesting routine you created yourself with fellow Delphi developers!