Checking Windows Version on W10
Asked Answered
O

2

11

Does anyone know if TOSVersion.Name still works on Windows 10?

I have a vcl application that has a form show event that gets the operating system details and displays them in a TMemo box using TOSVersion record from SysUtils.

with mmoOSInfo.Lines do 
 begin
    Clear;
    Add(TOSVersion.ToString);
    Add('');
    Add('Architecture: ' + OSArchitectureToStr(TOSVersion.Architecture));
    Add('Platform: ' + OSPlatformToStr(TOSVersion.Platform) +
     IntToStr(PlatformFromPointer));
    Add('Build: ' + IntToStr(TOSVersion.Build));
    Add('Major: ' + IntToStr(TOSVersion.Major));
    Add('Minor: ' + IntToStr(TOSVersion.Minor));
    Add('Name: ' + TOSVersion.Name);
    Add('Service Pack - Major: ' + IntToStr(TOSVersion.ServicePackMajor));
    Add('Service Pack - Minor: ' + IntToStr(TOSVersion.ServicePackMinor));
 end;

The code executes without any issues on XP (Yes we're still using it (hangs head in shame)), Vista, Windows 7, Windows 8.1, Desktop PC's, laptops and Surface Pro's but not when installed on Windows 10.

When I debug using paserver, TOSVersion.Name comes back as := 'Windows 8'. Am I doing something wrong or am I expecting too much for TOSVersion to detect Windows 10? No exception is being triggered. Of the 2 x Windows 10 machines I have access to, one migration path was from Windows 8.1, the other one however was from Windows 7.

Many Thanks

Occlusion answered 3/9, 2015 at 14:46 Comment(2)
jpetermugaas.com/versionhelpers.htmlShillelagh
Sorry, for the above link - I just tested that library on Win 10 and it is NOT working!Shillelagh
A
12

Two things stop your code from returning the correct version:

  1. The XE8 RTL that you use predates Windows 10 and so has no knowledge of Windows 10.
  2. Your executable does not manifest itself as supporting Windows 10, and so GetVersionEx, which TOSVersion relies upon, will lie about the version.

It so happens that XE8 update 1, I believe, changes the version detection to use NetWkstaGetInfo which is not subject to this version lie. Although the call to NetWkstaGetInfo does leak memory, but that's probably not important since it is only called once.

Some links relating to this subject:

If you absolutely must report the version to the user, then you have a variety of options:

  • Add the supportedOS option to your manifest and include the GUID for Windows 10. That stops GetVersionEx from lying. Then use a modified version of TOSVersion, or some other means, to obtain the version.
  • Use a WMI query.
  • Call NetServerGetInfo.
  • Call NetWkstaGetInfo.
  • Call RtlGetVersion.

More details in this question: How to detect true Windows version? Although note that the accepted answer there is out-of-date.

As an example of the WMI approach, you could use this code:

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;
Alphanumeric answered 3/9, 2015 at 14:54 Comment(8)
@JerryDodge Not any more. The new version is what you need, no doubt, according to the marketing types.Alphanumeric
You have to love marketing :) - That aside. If I look at this link (msdn.microsoft.com/en-us/library/windows/desktop/…) there are other implications for adding the supportedOS entries to the manifest. It's maybe a good idea to add that to the answer.Hypertrophy
Many thanks Mr Heffermen. I am grateful for your impressive depth of Delphi programming knowledge.Occlusion
Another option is to read the version from kernel32.dll see msdn.microsoft.com/en-us/library/windows/desktop/…Boneyard
@DavidHeffernan: "the call to NetWkstaGetInfo does leak memory" - are you saying that NetWkstaGetInfo() itself leaks memory, or that TOSVersion leaks memory when calling NetWkstaGetInfo()? I have not looked at the TOSVersion implementation yet.Craving
I tried your code, and it compiles except for the ArchitectureDisplayName(SystemArchitecture). Neither function/variable/constant seems to be known. Which file do I have to include in the uses clause to fix this? I already included ActiveX.Nibble
@joris Those are privates from my own code base. You can remove that or substitute something of your ownAlphanumeric
"changes the version detection to use NetWkstaGetInfo" - God! They changed the method again?Shillelagh
T
3

Have you tried using a custom manifest ?

I use XE8 and have no issues with TOSVersion recognizing Windows 10 when using a manifest file that targets Windows 8.1 and Windows 10.

My custom manifest is made to make my application "Windows 10" aware.

It is the following :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    type="win32"
    name="MrTheV Dev"
    version="11.0.2804.9245"
    processorArchitecture="*"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="asInvoker"
          uiAccess="False"/>
        </requestedPrivileges>
    </security>
  </trustInfo>
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> 
        <application> 
            <!-- Windows 10 --> 
            <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
            <!-- Windows 8.1 -->
            <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
            <!-- Windows Vista -->
            <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> 
            <!-- Windows 7 -->
            <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
            <!-- Windows 8 -->
            <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
        </application> 
    </compatibility>
</assembly>

I got that from this link : https://msdn.microsoft.com/fr-fr/library/windows/desktop/dn481241(v=vs.85).aspx

And TOSVersion.Tostring on a Windows 10 1607 displays :

Windows (Version 10.0, Build 14393, 64-bit Edition)
Torrell answered 16/5, 2017 at 22:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.