13

In .NET Framework, to get the OS version you can use Environment.OSVersion with the Major and Minor values telling you the version of Windows (i.e 6.1 = Windows 7, 10.0 = Windows 10). Even though Windows 11 has been released (dev and beta channels) for over a month now, the documentation has not been updated to mention how to detect Windows 11.

For Windows API, GetVersion has been deprecated forever and even the version helper APIs only go up to IsWindows10OrGreater. Is there a simple check to figure out the major Windows version, in particular Windows 11? Someone had a similar question but for Delphi (How to detect Windows 11 using Delphi 10.3.3) and the accepted answer was all hacks. Why the hell is it so difficult for Microsoft to provide a simple API to just return the current system version? GetVersion should never have been deprecated.

barneyAgumble
  • 167
  • 1
  • 7
  • its now more about compatibility and keeping things working than highlighting the latest windows version – Daniel A. White Sep 03 '21 at 01:35
  • 2
    Instead of detecting Windows 11, detect the feature you are trying to use. – Raymond Chen Sep 03 '21 at 02:07
  • 6
    @RaymondChen I respect you a lot and love your books and blog and quite honestly humbled you responded to my question, but that is a terrible answer. Windows is too big for that. – barneyAgumble Sep 03 '21 at 02:18
  • 1
    If you are checking for Windows 11 in order to decide whether to call some new Windows 11 API, then the thing to do is to check for the presence of the API you are thinking of calling. – Raymond Chen Sep 03 '21 at 02:35
  • 3
    In theory, sure. But there's more than just detected APIs. And with .NET that's not even easy to do. But also what about kernel features from user mode? How can my installer detect which driver to install? There are significant differences between Win 7, 8 and 10 (and probably 11) with kernel features that you basically have to compile separate drivers to target those features properly (WFP and minifilters come to mind). So I increase my complexity in the kernel or Microsoft could just make a simple API that returns 7, 8, 10 or 11 and I follow a simple user mode path. – barneyAgumble Sep 03 '21 at 03:10
  • @RaymondChen, in my case, the feature I'm using is a collection of virtual machines. When a problem report comes in, I need to know which one to spin up to try to reproduce the issue. – Mark Mar 14 '22 at 21:12
  • 1
    @Mark For that purpose, use `Windows.System.Profile.AnalyticsInfo.VersionInfo`. If you get a version that doesn't match the version of any of your VMs, then you know that you need to go create a new VM. – Raymond Chen Mar 14 '22 at 21:27
  • @RaymondChen Also a big fan of your work and while I do as you suggest and detect the presence of APIs, in the past I've had to work around subtle changes in API behavior or bugs in the APIs. While it's not a common problem, it does happen, and having a `WhichWindowsVersionIsThis()` is always going to be necessary in some cases. I don't really understand why MS make version tests such a difficult moving target. It seems like it should be simple. – Benj Nov 21 '22 at 08:36
  • 1
    @Benj I agree! We have a DLL that broke in Windows 11 and found a workaround via "proxy DLL", but we need to detect Windows 10 vs 11 in order to know when to use the proxy. Each time Microsoft releases a new version of Windows they shouldn't pretend to be the previous version yet break compatibility with things, it's so annoying! – Kevin North Mar 04 '23 at 20:53

9 Answers9

12

This most likely isn't perfect, but it's a workaround I've been using for a bit:

Environment.OSVersion.Version.Build >= 22000;

Windows 11 starts at build number 22000 and Windows 10 Insider builds end roughly at build number 21390, so this will only detect Windows 11 builds as far as I'm aware.

Do check whether it even is a Windows system in the first place though, another OS might have a higher build number.


As this answer is still active let's talk a bit more about the upsides and downsides of this solution:

  • Checking for feature availability instead of arbitrarily drawing a line at "Windows 11" should be your first thought, however detecting a specific OS version does have some merit in some cases as well.
  • As mentioned in the comments, this also will not distinguish Windows Server and return true for Windows Server 2019. A quick search for how to detect a Server OS returned this answer, which I have not tested but which looks reasonable at first glance.
  • Another point brought up in a comment that I should add is build numbers of a next version of Windows, which will obviously return true in this case as they will be higher. If you really want to detect Windows 11 exactly and don't want Windows 12 (or whatever comes after it) to show up at any cost this is not the solution for you.
  • Lastly, the question came up whether Windows 10 build numbers will get to 22000 or higher, which was ("At least for now") refuted by a Microsoft Employee here.
  • You can find the current build numbers for each Windows 10 update here or here and for each Windows 11 update here or here.
LW001
  • 2,452
  • 6
  • 27
  • 36
  • 3
    That's what I found too but still feels very weak. Windows 10 will be around for a lot longer so I don't see how the build numbers won't conflict. – barneyAgumble Sep 03 '21 at 02:21
  • I don't think they will, now that the actual release of 11 is a month away and releases of 10 have switched to twice a year, each having increased the build number by one (21H1 being 19043 and 20H2 being 19042) with only about 5 years to go I don't see them going over that with 10. – LW001 Sep 03 '21 at 02:37
  • 1
    I tested this code and it failed. It returned wrong build number – gil123 Dec 03 '21 at 17:24
  • It seems that it is safer to use `Environment.OSVersion.Version.Build = 22000;` for Windows 11, because `> 22000` will hit the next version of Windows at some point. – Maris B. May 24 '22 at 12:11
  • 1
    @gil123 - you must add the `supportedOS` GUIDs to your the manifest file to get the correct results. Read more here: https://learn.microsoft.com/en-us/windows/win32/sysinfo/targeting-your-application-at-windows-8-1 and https://stackoverflow.com/questions/26151534/whats-the-supportedos-guid-for-windows-10 – Maris B. May 24 '22 at 12:14
  • 1
    This no longer seems to be correct - I'm seeing build strings for windows 11 that are less than 22000, 6.2.9200.0 – David Jun 16 '22 at 22:51
  • This solution leaves a lot to be desired. For example, how do you reliably distinguish between server & consumer/desktop editions of Windows? Those appear to share the same build number space. For example, Windows 10 is between 10000 - 19000 and Windows 2019 is in the 17000 range. How do you disambiguate? – void.pointer Jul 18 '22 at 19:30
3

At our company we have decided the ONLY reliable way to detect the Windows version is to shell out to a command prompt and run "systeminfo". The reasoning is as follows.

  1. In Microsoft's official documentation for Version API helper functions ( https://learn.microsoft.com/en-us/windows/win32/sysinfo/operating-system-version ), it will NOT detect Windows 11, and Microsoft recommends testing for the presence/absence of a feature instead. For this reason we believe Microsoft will deliberately try to block programmers from getting an accurate version.

  2. LW001 suggests checking for build #22000, but one user said it doesn't work, and another said it was weak. In light of (1) above, it doesn't seem a safe long term solution.

  3. "systeminfo", on the other hand, is a Microsoft tool built-in to Windows that is specifically designed to assist with debugging a PC's configuration. As such, unlike (1), it necessarily must report correct Windows version information.

The only downside is that (3) takes a couple of seconds to run, but we are doing that invisibly in the background while showing our splash screen. We actually implemented the check in Delphi instead of .Net, but the basic outline is the same and looks like this:

BatFile := MakeTempPath('Win11Check-%d.bat');
ResultFile := MakeTempPath('Win11Result-%d.txt');
SaveStringToFile(BatFile, AnsiString('systeminfo>"' + ResultFile + '"'));
LaunchProcessAndWaitForExit(BatFile, '', True);  
Results := LoadWideStringFromFile(ResultFile);
OsName := GetStringBetween(Results, 'OS Name', #13) + ' 0';

We create a batch file that runs systeminfo and dumps the result into a result file. We run that batch file invisibly in the background and wait for the process to terminate. Then we read the result file contents as a string. Finally, we grab the Windows version by finding the substring between "OS Name" and the next carriage return. There's actually a colon and some spaces still in the string but you get the idea... we just look for an integer... "10" vs "11"... or even "2019" or "2022" in case of Windows Server.

In summary, Microsoft believes that every time it implements a new operating system, it can just emulate the previous one perfectly and even emulate its old version numbers. In practice, programmers have repeatedly found compatibility problems specific to a version (in our case we are seeing a repeatable and definite problem specifically with Windows 11). Thus, we believe Microsoft's strategy (as mentioned in (1) at the top) is wrong, and that "systeminfo" is our best bet in reliably detecting the current Windows.

Kevin North
  • 194
  • 1
  • 6
  • 2
    I've tried all other suggestions and finally came up with your solution because I was not able to distinguish betweeen Windows10 and 11 any other way. To catch the output of _systeminfo_ in .NET see this post: [Process.start: how to get the output?](https://stackoverflow.com/a/4291965/7634331) – Matumba Apr 20 '23 at 06:37
  • FOLLOW UP: Although our 10/11 detection continues to work perfectly, we are now getting reports of our customers getting failures under Windows 10. We suspect that Microsoft may be starting to deploy some W11 features into W10 via Windows Update. Hence Microsoft's recommendation that we check for the presence/absence of features. If only we knew what feature to test for!!! As a temporary "backdoor," we implemented a way for a user to override the result of the 10/11 check, providing a potential temporary fix, until we can figure out code that will work in any system. – Kevin North Jul 28 '23 at 14:56
2

Use this code:

public static bool IsWindows11()
{
    var reg = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");

    var currentBuildStr = (string)reg.GetValue("CurrentBuild");
    var currentBuild = int.Parse(currentBuildStr);

    return currentBuild >= 22000;
}

This is based on my findings on HKLM\Software\Microsoft\Windows NT\CurrentVersion: What's the difference between CurrentBuild and CurrentBuildNumber?

And note that this code can throw exceptions in case that the registry key does not exist or in case the value is not number. This should not happen in a normal system. But if you want to be safe, you can warp it in a try-catch block.

gil123
  • 512
  • 6
  • 12
  • 1
    This no longer seems to be correct - I'm seeing build strings for windows 11 that are less than 22000, 6.2.9200.0 – David Jun 16 '22 at 22:56
1

You can use RtlGetVersion() in ntdll.dll, which is not subject to OS compatibility manifestation, like GetVersion/Ex() and other similar APIs are (and especially since there is no supportedOS GUID defined for Windows 11 yet).

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I recognized that as well which makes me think that Microsoft doesn't consider Windows 11 a significant update from Windows 10 21H1/2. Basically just a yearly update but with a new name (along with seemingly arbitrary hardware restrictions added on). – barneyAgumble Sep 03 '21 at 02:54
  • 1
    Personally, In my code the version that works is Remy's answer. I am actually using stack of GetVersion()-> RtlGetVersion() -> kernel32.dll version. It tries to detect inconsistencies and gets best version. For Win10 in particular, version is still 10 but build is 22000 or superior. However not sure if that's enough information. What will be the next Win 10 build? Over 22000 or is now stuck to latest 19043? M$ mess as usual. Why make simple when they can make complicated. – Niki Oct 05 '21 at 09:40
1

A VB.NET method to get the OS name using the version number returned from Environment.OSVersion.Version

Public Shared Function GetOsFriendlyName(osVersion As Version) As String

    Select Case osVersion.Major

        Case 10

            Select Case osVersion.Minor

                Case 0

                    Select Case osVersion.Build

                        Case >= 20000
                            Return "Windows 11"

                        Case Else
                            Return "Windows 10"

                    End Select

            End Select

        Case 6

            Select Case osVersion.Minor

                Case 3
                    Return "Windows 8.1"

                Case 2
                    Return "Windows 8"

                Case 1
                    Return "Windows 7"

                Case 0
                    Return "Windows Vista"

            End Select

        Case 5
            Return "Windows XP"

    End Select

    Return "Windows"

End Function

The app should set the OS compatibility supportedOS in the app.manifest

Ahmed Osama
  • 348
  • 1
  • 9
0

RtlGetVersion() gave same results for Windows 10 and 11 as for me. I called it from the C# code as described here https://stackoverflow.com/a/49641055. I tried the Caption property of the WMI's Win32_OperatingSystem class.

using (var objOS = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem"))
{
    foreach (ManagementObject objMgmt in objOS.Get())
    {
        Console.WriteLine("{0}: {1}", objMgmt.Properties["Caption"].Name, objMgmt.Properties["Caption"].Value);
    }
}

For Windows 10 I got "Microsoft Windows 10 Enterprise" and for Windows 11 "Microsoft Windows 11 Pro". Probably you could use the Name property but it contains excessive information like Windows folder and system drive or something like that.

0

Windows 10 start with build 10240 and end with build 19044
Windows 11 start with build 22000

Source

dandan
  • 123
  • 1
  • 6
-1
include <iostream>
#include <sstream>
#include <bitset>
#include <windows.h>

bool isWin11()
{   
    // the container in the registry to look for
    std::wstring regstr(L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
    // the key to look for
    std::wstring strkey(L"CurrentBuild");
    // handle to the container
    HKEY hKey = nullptr;
    // get the handle if it is there
    LONG lres = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regstr.c_str(), 0, KEY_READ, &hKey);    
    // the return value is it is success, convert to bool
    bool bExistsAndSuccess(lres == ERROR_SUCCESS);
    // if success get the value of CurrentBuild
    if(bExistsAndSuccess)
    {
        // not good if it stays bad
        std::wstring strValue(L"bad");        
        // c string buffer
        WCHAR szBuffer[1024]{ 0 };
        // size of c string
        DWORD dwBufferSize = sizeof(szBuffer);
        // error is now still ok
        ULONG nError = 0;
        // get the value in szBuffer
        nError = RegQueryValueEx(hKey, strkey.c_str(), 0, NULL, (LPBYTE)szBuffer, &dwBufferSize);
        // if success the buffer is copied to the string
        if (ERROR_SUCCESS == nError)
        {
            // copy the szBuffer to the c string type
            strValue = szBuffer;
            // convert to int
            int n = std::stoi(strValue);
            // if higher or equal to 22000 it is Windows 11
            return(n >= 22000);
        }
        else
            // failure for some reason
            return false;
    }
    // failed to get the container
    return false;
}




int main()
{
    if (isWin11())
    {
        printf("Is Windows 11");
    }

    return 0;
}
-1

This is a class I use to get useful information about the current environment. Requires reference to nuget package System.Management.

NB: The Caption field in Win32_OperatingSystem is Microsoft Windows 11 [Pro/Home/Etc], which can be used to detect Win11.

using System.Management;
using System.Runtime.Versioning;

[SupportedOSPlatform("windows")]
public static class OSInfo
{
    public static bool IsVirtualMachine { get; }
    public static bool IsDomainJoined { get; }
    public static bool IsWin11 { get; }
    public static string Version { get; }
    
    static OSInfo()
    {
        using (var searcher = new ManagementObjectSearcher("Select Manufacturer,Model,PartOfDomain from Win32_ComputerSystem"))
        using (var items = searcher.Get())
        {
            IsVirtualMachine = items.Cast<ManagementBaseObject>()
                .Any(item =>
                {
                    var manufacturer = ((string?)item["Manufacturer"])?.ToLowerInvariant() ?? string.Empty;
                    var model = ((string?)item["Model"])?.ToLowerInvariant() ?? string.Empty;
                    
                    return (manufacturer == "microsoft corporation" && model.Contains("virtual"))
                           || manufacturer.Contains("vmware")
                           || model == "virtualbox";
                });
        
            IsDomainJoined = items.Cast<ManagementBaseObject>()
                .Any(item => item["PartOfDomain"] is true);
        }

        using (var searcher = new ManagementObjectSearcher("select Caption,Version from Win32_OperatingSystem"))
        using (var items = searcher.Get())
        {
            IsWin11 = items.Cast<ManagementBaseObject>()
                .Any(obj => ((string) obj["Caption"]).StartsWith("Microsoft Windows 11"));
        
            Version = items.Cast<ManagementBaseObject>()
                .Select(obj =>
                {
                    var caption = (string)obj["Caption"];
                    caption = caption.Replace("Microsoft Windows ", string.Empty);
                    var version = (string)obj["Version"];
                    // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
                    var platform = Environment.OSVersion.Platform switch
                    {
                        PlatformID.Win32S => "Win32S",
                        PlatformID.Win32NT => "Windows NT",
                        PlatformID.WinCE => "Windows CE",
                        _ => throw new ArgumentException($"Unknown platform {Environment.OSVersion.Platform}")
                    };
                    return $"Microsoft {platform} {caption} {version}";
                })
                .First();
        }
    }
}
Jeremy Morren
  • 615
  • 9
  • 23