55 *
66 * Copyright (C) 2001-2015, Peter Johnson (@delphidabbler).
77 *
8- * $Rev$
9- * $Date$
8+ * $Rev: 2002 $
9+ * $Date: 2015-11-30 14:45:35 +0000 (Mon, 30 Nov 2015) $
1010 *
1111 * This unit contains various static classes, constants, type definitions and
1212 * global variables for use in providing information about the host computer and
2727 * OS. When run on Windows 8.1 and later details of the actual host
2828 * operating system are always returned and the emulated OS is ignored.
2929 *
30- * 4: ** IMPORTANT **
31- * This version of the code was an attempt to get it to detect and report
32- * Windows 10. Try as I might, I can't get this to work. So this version
33- * is released as beta code to use at your own risk. If anyone can fix it,
34- * please let me know.
35- *
3630 * ACKNOWLEDGEMENTS
3731 *
3832 * Thanks to the following who have contributed to this project:
@@ -576,11 +570,25 @@ TPJOSInfo = class(TObject)
576570 class function MinorVersion : Integer;
577571
578572 // / <summary>Returns the host OS's build number.</summary>
573+ // / <remarks>A return value of 0 indicates that the build number can't be
574+ // / found.</remarks>
579575 class function BuildNumber : Integer;
580576
581577 // / <summary>Returns the name of any installed OS service pack.</summary>
582578 class function ServicePack : string;
583579
580+ // / <summary>Returns the name of any installed OS service pack along with
581+ // / other similar, detectable, updates.</summary>
582+ // / <remarks>
583+ // / <para>Windows has added significant OS updates that bump the build
584+ // / number but do not declare themselves as service packs: e.g. the Windows
585+ // / 10 TH2 update.</para>
586+ // / <para>This method is used to report such updates in addition to
587+ // / updates that declare themselves as service packs, while the ServicePack
588+ // / method only reports declared 'official' service packs.</para>
589+ // / </remarks>
590+ class function ServicePackEx : string;
591+
584592 // / <summary>Returns the major version number of any NT platform service
585593 // / pack.</summary>
586594 // / <remarks>0 is returned in no service pack is installed, if the host OS
@@ -1201,6 +1209,11 @@ implementation
12011209 InternalCSDVersion: string = ' ' ;
12021210 // Internal variable recording processor architecture information
12031211 InternalProcessorArchitecture: Word = 0 ;
1212+ // Internal variable recording additional update information.
1213+ // ** This was added because Windows 10 TH2 doesn't declare itself as a
1214+ // service pack, but is a significant update.
1215+ // ** At present this variable is only used for Windows 10.
1216+ InternalExtraUpdateInfo: string = ' ' ;
12041217
12051218// Flag required when opening registry with specified access flags
12061219{ $IFDEF REGACCESSFLAGS}
@@ -1211,7 +1224,7 @@ implementation
12111224// Tests Windows version (major, minor, service pack major & service pack minor)
12121225// against the given values using the given comparison condition and return
12131226// True if the given version matches the current one or False if not
1214- // Assumes VerifyVersionInfo API function is available
1227+ // Assumes VerifyVersionInfo & VerSetConditionMask APIs functions are available
12151228// Adapted from code from VersionHelpers.pas
12161229// by Achim Kalwa <delphi@achim-kalwa.de> 2014-01-05
12171230function TestWindowsVersion (wMajorVersion, wMinorVersion,
@@ -1255,8 +1268,25 @@ function TestWindowsVersion(wMajorVersion, wMinorVersion,
12551268 );
12561269end ;
12571270
1271+ // Checks if given build number matches that of the current OS.
1272+ // Assumes VerifyVersionInfo & VerSetConditionMask APIs functions are available
1273+ function IsBuildNumber (BuildNumber: DWORD): Boolean;
1274+ var
1275+ OSVI: TOSVersionInfoEx;
1276+ POSVI: POSVersionInfoEx;
1277+ ConditionalMask: UInt64;
1278+ begin
1279+ Assert(Assigned(VerSetConditionMask) and Assigned(VerifyVersionInfo));
1280+ FillChar(OSVI, SizeOf(OSVI), 0 );
1281+ OSVI.dwOSVersionInfoSize := SizeOf(OSVI);
1282+ OSVI.dwBuildNumber := BuildNumber;
1283+ POSVI := @OSVI;
1284+ ConditionalMask := VerSetConditionMask(0 , VER_BUILDNUMBER, VER_EQUAL);
1285+ Result := VerifyVersionInfo(POSVI, VER_BUILDNUMBER, ConditionalMask);
1286+ end ;
1287+
12581288// Checks if the OS has the given product type.
1259- // Assumes VerifyVersionInfo and VerSetConditionMask API functions are available
1289+ // Assumes VerifyVersionInfo & VerSetConditionMask APIs functions are available
12601290function IsWindowsProductType (ProductType: Byte): Boolean;
12611291var
12621292 ConditionalMask: UInt64;
@@ -1445,23 +1475,6 @@ function GetCurrentVersionRegStr(ValName: string): string;
14451475 Result := GetRegistryString(HKEY_LOCAL_MACHINE, cWdwCurrentVer, ValName);
14461476end ;
14471477
1448- // Reads build number from registry for NT OSs only.
1449- function GetNTBuildNumberFromReg : LongWord;
1450- var
1451- BuildStr: string;
1452- begin
1453- BuildStr := GetRegistryString(
1454- HKEY_LOCAL_MACHINE, CurrentVersionRegKeys[True], ' CurrentBuildNumber'
1455- );
1456- Result := StrToIntDef(BuildStr, 0 );
1457- if Result <> 0 then
1458- Exit;
1459- BuildStr := GetRegistryString(
1460- HKEY_LOCAL_MACHINE, CurrentVersionRegKeys[True], ' CurrentBuild'
1461- );
1462- Result := StrToIntDef(BuildStr, 0 );
1463- end ;
1464-
14651478// Initialise global variables with extended OS version information if possible.
14661479procedure InitPlatformIdEx ;
14671480
@@ -1477,6 +1490,18 @@ procedure InitPlatformIdEx;
14771490 GetVersionEx: TGetVersionEx; // pointer to GetVersionEx API function
14781491 GetProductInfo: TGetProductInfo; // pointer to GetProductInfo API function
14791492 SI: TSystemInfo; // structure from GetSystemInfo API call
1493+ const
1494+ // Known windows build numbers.
1495+ // Source: https://en.wikipedia.org/wiki/Windows_NT
1496+ // for Vista and Win 7 we have to add service pack number to these values to
1497+ // get actual build number
1498+ WinVistaBaseBuild = 6000 ;
1499+ Win7BaseBuild = 7600 ;
1500+ // for Win 8 onwards we just use the build numbers as is
1501+ Win8Build = 9200 ;
1502+ Win8Point1Build = 9600 ;
1503+ Win10TH1Build = 10240 ;
1504+ Win10TH2Build = 10586 ;
14801505begin
14811506 // Load version query functions used externally to this routine
14821507 VerSetConditionMask := LoadKernelFunc(' VerSetConditionMask' );
@@ -1505,49 +1530,68 @@ procedure InitPlatformIdEx;
15051530 InternalMajorVersion, InternalMinorVersion,
15061531 Win32ServicePackMajor, Win32ServicePackMinor
15071532 );
1508- if Win32ServicePackMajor > 0 then
1509- // tried to read this info from registry, but for some weird reason the
1510- // required value is reported as not existant by TRegistry, even though it
1511- // is present in registry
1512- InternalCSDVersion := Format(' Service Pack %d' , [Win32ServicePackMajor]);
15131533 // NOTE: It's going to be very slow to test for all possible build numbers,
1514- // so I've just hard wired them using the information at
1534+ // so I've just narrowed the search down using the information at
15151535 // http://en.wikipedia.org/wiki/Windows_NT
15161536 case InternalMajorVersion of
15171537 6 :
15181538 begin
15191539 case InternalMinorVersion of
1520- { $IFDEF DEBUG_NEW_API}
15211540 0 :
1522- InternalBuildNumber := 6000 + Win32ServicePackMajor; // Vista
1541+ // Vista
1542+ InternalBuildNumber := WinVistaBaseBuild + Win32ServicePackMajor;
15231543 1 :
1524- InternalBuildNumber := 7600 + Win32ServicePackMajor; // Windows 7
1544+ // Windows 7
1545+ InternalBuildNumber := Win7BaseBuild + Win32ServicePackMajor;
15251546 2 :
1547+ // Windows 8 (no known SPs)
15261548 if Win32ServicePackMajor = 0 then
1527- InternalBuildNumber := 9200 ; // Windows 8 (no known SPs)
1528- { $ENDIF}
1549+ InternalBuildNumber := Win8Build;
15291550 3 :
1551+ // Windows 8.1 (no known SPs)
15301552 if Win32ServicePackMajor = 0 then
1531- InternalBuildNumber := 9600 ; // Windows 8.1 (no known SPs)
1553+ InternalBuildNumber := Win8Point1Build;
15321554
15331555 end ;
1556+ if Win32ServicePackMajor > 0 then
1557+ // ** Tried to read this info from registry, but for some weird
1558+ // reason the required value is reported as non-existant by
1559+ // TRegistry, even though it is present in registry.
1560+ // ** Seems there is some kind of regitry "spoofing" going on (see
1561+ // below.
1562+ InternalCSDVersion := Format(
1563+ ' Service Pack %d' , [Win32ServicePackMajor]
1564+ );
15341565 end ;
15351566 10 :
15361567 begin
15371568 case InternalMinorVersion of
15381569 0 :
15391570 begin
15401571 // TODO: Revist when server version released to check if same build
1541- // number
1542- if Win32ServicePackMajor = 0 then
1543- InternalBuildNumber := 10240 ; // Windows 10 (no known SPs)
1572+ // number(s)
1573+ // Windows 10 TH1 branch release
1574+ if IsBuildNumber(Win10TH1Build) then
1575+ InternalBuildNumber := Win10TH1Build
1576+ // Windows 10 TH2 branch release
1577+ else if IsBuildNumber(Win10TH2Build) then
1578+ begin
1579+ InternalBuildNumber := Win10TH2Build;
1580+ InternalExtraUpdateInfo := ' TH2: November Update' ;
1581+ end ;
15441582 end ;
15451583 end ;
15461584 end ;
15471585 end ;
1548- // Failed to "guess" at build number: get it from registry
1549- if InternalBuildNumber = 0 then
1550- InternalBuildNumber := GetNTBuildNumberFromReg;
1586+
1587+ // ** If InternalBuildNumber is 0 when we get here then we failed to get it
1588+ // We no longer look in registry as of SVN commit r2001, because this is
1589+ // can get spoofed. E.g. when running on Windows 10 TH2 registry call is
1590+ // returning build number of 7600 even though regedit reveals it to be
1591+ // 10586 !
1592+ // So we must now consider a build number of 0 as indicating an unknown
1593+ // build number.
1594+ // ** Seems like more registry spoofing (see above).
15511595
15521596 // Test possible product types to see which one we have
15531597 if IsWindowsProductType(VER_NT_WORKSTATION) then
@@ -2327,6 +2371,15 @@ class function TPJOSInfo.ServicePack: string;
23272371 end ;
23282372end ;
23292373
2374+ class function TPJOSInfo.ServicePackEx : string;
2375+ begin
2376+ Result := ServicePack;
2377+ if Result = ' ' then
2378+ Result := InternalExtraUpdateInfo
2379+ else
2380+ Result := Result + ' , ' + InternalExtraUpdateInfo;
2381+ end ;
2382+
23302383class function TPJOSInfo.ServicePackMajor : Integer;
23312384begin
23322385 Result := Win32ServicePackMajor;
0 commit comments