Getting Windows OS version programmatically
Asked Answered
V

6

12

I am trying to fetch Windows version with C# on my Windows 10 machine.

I always get those values (with C#\C++):

Major: 6

Minor: 2

Which is Windows 8 OS, accordingly to MSDN

C# code:

var major = OperatingSystem.Version.Major
var minor  = OperatingSystem.Version.Minor

C++ code

void print_os_info()
{
    //https://mcmap.net/q/104793/-check-windows-version
    OSVERSIONINFOW info;
    ZeroMemory(&info, sizeof(OSVERSIONINFOW));
    info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);

    LPOSVERSIONINFOW lp_info = &info;
    GetVersionEx(lp_info);

    printf("Windows version: %u.%u\n", info.dwMajorVersion, info.dwMinorVersion);
}

Windows 10 suppose to be with those:

Major: 10

Minor: 0*

  • (When I am taking a dump file from running process I can see that the OS version of that file is set to 10.0)

built by: 10.0.10586.0 (th2_release.151029-1700)

What am I missing here?

Varian answered 8/6, 2016 at 10:58 Comment(6)
This might be relevant: from the page you linked to - "Applications not manifested for Windows 8.1 or Windows 10 will return the Windows 8 OS version value (6.2)."Shadwell
I suppose you are right, missed itVarian
My application is console, so I only got this App.config file (no manifset)Varian
Microsoft has promised for a long time that they'll defeat version checks. It is quite an appcompat nightmare, they create a new Windows version that is as compatible as possible with the previous one and then programs still don't work because they check the version number and refuse to run when it is one it doesn't recognize. the only way to get 8.3 is to use Editbin.exe to change the subsystem version to 10.0. Your program will now only run on Win10. So no point in checking the version anymore. The writing is on the wall, don't do it.Richela
Version numbers are a compatibility nightmareIlluminance
@PavelDurov It is a bit strange to have my answer "unaccepted" when the question is "I am trying to fetch Windows version with C# on my Windows 10 machine" and the answer I provided is for C#. Also, the question is tagged C#, not C++. The new accepted answer is in C++, which is only used in an example (next to the C# example). No mention that C++ was preferred and no C++ tag. IMHO, the new accepted answer does not answer your question, as it is stated.Statvolt
S
5

As the accepted answer is only for C#, here is a solution for C++.

It uses the RtlGetVersion in the ntdll.dll that uses the same structure as GetVersionEx (name is different, but the elements are the same) and gives you the correct version. As this function is normally used for driver development, the function is declared in the DDK and not in the SDK. So I used a dynamic solution to call the function. Please be aware that the ntdll.dll is loaded and released in every call. So if you need the function more often, keep the library loaded.

The structure pOSversion is pointing to must be initialized like for GetVersionEx.

BOOL GetTrueWindowsVersion(OSVERSIONINFOEX* pOSversion)
{
   // Function pointer to driver function
   NTSTATUS (WINAPI *pRtlGetVersion)(
      PRTL_OSVERSIONINFOW lpVersionInformation) = NULL;

   // load the System-DLL
   HINSTANCE hNTdllDll = LoadLibrary("ntdll.dll");

   // successfully loaded?
   if (hNTdllDll != NULL)
   {
      // get the function pointer to RtlGetVersion
      pRtlGetVersion = (NTSTATUS (WINAPI *)(PRTL_OSVERSIONINFOW))
            GetProcAddress (hNTdllDll, "RtlGetVersion");

      // if successfull then read the function
      if (pRtlGetVersion != NULL)
         pRtlGetVersion((PRTL_OSVERSIONINFOW)pOSversion);

      // free the library
      FreeLibrary(hNTdllDll);
   } // if (hNTdllDll != NULL)

   // if function failed, use fallback to old version
   if (pRtlGetVersion == NULL)
      GetVersionEx((OSVERSIONINFO*)pOSversion);

   // always true ...
   return (TRUE);
} // GetTrueWindowsVersion
Scandalmonger answered 12/2, 2018 at 15:20 Comment(1)
This is the best solution I've found anywhere!Dug
S
16

In my scenario I needed my application to capture computer info for possible bug-reports and statistics.

I did not find the solutions where an application manifest had to be added satisfactory. Most of the suggestions I found while googling this suggested just that, unfortunately.

Thing is, when using a manifest, each OS version has to be added manually to it in order for that particular OS version to be able to report itself at runtime.

In other words, this becomes a race condition: A user of my app may very well be using a version of my app that pre-dates the OS in use. I would have to upgrade the app immediately when a new OS version was launched by Microsoft. I would also have to force the users to upgrade the app at the same time as they updated the OS.

In other words, not very feasible.

After browsing through the options I found some references (surprisingly few compared to the app manifest) that instead suggested using registry lookups.

My (chopped down) ComputerInfo class with only WinMajorVersion, WinMinorVersion and IsServer properties looks like this:

using Microsoft.Win32;

namespace Inspection
{
    /// <summary>
    /// Static class that adds convenient methods for getting information on the running computers basic hardware and os setup.
    /// </summary>
    public static class ComputerInfo
    {
        /// <summary>
        ///     Returns the Windows major version number for this computer.
        /// </summary>
        public static uint WinMajorVersion
        {
            get
            {
                dynamic major;
                // The 'CurrentMajorVersionNumber' string value in the CurrentVersion key is new for Windows 10, 
                // and will most likely (hopefully) be there for some time before MS decides to change this - again...
                if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", out major))
                {
                    return (uint) major;
                }

                // When the 'CurrentMajorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion'
                dynamic version;
                if (!TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out version))
                    return 0;

                var versionParts = ((string) version).Split('.');
                if (versionParts.Length != 2) return 0;
                uint majorAsUInt;
                return uint.TryParse(versionParts[0], out majorAsUInt) ? majorAsUInt : 0;
            }
        }

        /// <summary>
        ///     Returns the Windows minor version number for this computer.
        /// </summary>
        public static uint WinMinorVersion
        {
            get
            {
                dynamic minor;
                // The 'CurrentMinorVersionNumber' string value in the CurrentVersion key is new for Windows 10, 
                // and will most likely (hopefully) be there for some time before MS decides to change this - again...
                if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMinorVersionNumber",
                    out minor))
                {
                    return (uint) minor;
                }

                // When the 'CurrentMinorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion'
                dynamic version;
                if (!TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out version))
                    return 0;

                var versionParts = ((string) version).Split('.');
                if (versionParts.Length != 2) return 0;
                uint minorAsUInt;
                return uint.TryParse(versionParts[1], out minorAsUInt) ? minorAsUInt : 0;
            }
        }

        /// <summary>
        ///     Returns whether or not the current computer is a server or not.
        /// </summary>
        public static uint IsServer
        {
            get
            {
                dynamic installationType;
                if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "InstallationType",
                    out installationType))
                {
                    return (uint) (installationType.Equals("Client") ? 0 : 1);
                }

                return 0;
            }
        }

        private static bool TryGetRegistryKey(string path, string key, out dynamic value)
        {
            value = null;
            try
            {
                using(var rk = Registry.LocalMachine.OpenSubKey(path))
                {
                    if (rk == null) return false;
                    value = rk.GetValue(key);
                    return value != null;
                }
            }
            catch
            {
                return false;
            }
        }
    }
}
Statvolt answered 9/6, 2016 at 3:55 Comment(2)
OpenSubKey returns a RegistryKey instance, which implements IDisposable. Being that the case, its usage should be wrapped in a using construct to ensure proper deterministic disposal.Sneak
@JesseC.Slicer I think you are right about that. I'll adjust. Thanks.Statvolt
G
5

You'll need to add an app.manifest to your application:

enter image description here

enter image description here

then uncomment the following line:

<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
Gemoets answered 8/6, 2016 at 11:6 Comment(3)
My application is console, so I only got this App.config fileVarian
@PavelDurov works fine for me on Windows 10 using Environment.OSVersion.Version.Major & Environment.OSVersion.Version.MinorGemoets
Ok, I'll see maybe I messed up something hereVarian
D
5
Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentBuildNumber", string.Empty).ToString()

same code for all OSes from XP till current 10.16299, over scenarios not properly work from windows 8

The OSVersion property reports the same version number (6.2.0.0) for both Windows 8 and Windows 8.1 and the same major and minor version number for Windows 10.

https://msdn.microsoft.com/library/system.environment.osversion.aspx

Danged answered 5/12, 2017 at 8:33 Comment(0)
S
5

As the accepted answer is only for C#, here is a solution for C++.

It uses the RtlGetVersion in the ntdll.dll that uses the same structure as GetVersionEx (name is different, but the elements are the same) and gives you the correct version. As this function is normally used for driver development, the function is declared in the DDK and not in the SDK. So I used a dynamic solution to call the function. Please be aware that the ntdll.dll is loaded and released in every call. So if you need the function more often, keep the library loaded.

The structure pOSversion is pointing to must be initialized like for GetVersionEx.

BOOL GetTrueWindowsVersion(OSVERSIONINFOEX* pOSversion)
{
   // Function pointer to driver function
   NTSTATUS (WINAPI *pRtlGetVersion)(
      PRTL_OSVERSIONINFOW lpVersionInformation) = NULL;

   // load the System-DLL
   HINSTANCE hNTdllDll = LoadLibrary("ntdll.dll");

   // successfully loaded?
   if (hNTdllDll != NULL)
   {
      // get the function pointer to RtlGetVersion
      pRtlGetVersion = (NTSTATUS (WINAPI *)(PRTL_OSVERSIONINFOW))
            GetProcAddress (hNTdllDll, "RtlGetVersion");

      // if successfull then read the function
      if (pRtlGetVersion != NULL)
         pRtlGetVersion((PRTL_OSVERSIONINFOW)pOSversion);

      // free the library
      FreeLibrary(hNTdllDll);
   } // if (hNTdllDll != NULL)

   // if function failed, use fallback to old version
   if (pRtlGetVersion == NULL)
      GetVersionEx((OSVERSIONINFO*)pOSversion);

   // always true ...
   return (TRUE);
} // GetTrueWindowsVersion
Scandalmonger answered 12/2, 2018 at 15:20 Comment(1)
This is the best solution I've found anywhere!Dug
W
3

You can do this in C# the same way C++ answer has it

[SecurityCritical]
[DllImport("ntdll.dll", SetLastError = true)]
internal static extern bool RtlGetVersion(ref OSVERSIONINFOEX versionInfo);
[StructLayout(LayoutKind.Sequential)]
internal struct OSVERSIONINFOEX
{
    // The OSVersionInfoSize field must be set to Marshal.SizeOf(typeof(OSVERSIONINFOEX))
    internal int OSVersionInfoSize;
    internal int MajorVersion;
    internal int MinorVersion;
    internal int BuildNumber;
    internal int PlatformId;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    internal string CSDVersion;
    internal ushort ServicePackMajor;
    internal ushort ServicePackMinor;
    internal short SuiteMask;
    internal byte ProductType;
    internal byte Reserved;
}

...

var osVersionInfo = new OSVERSIONINFOEX { OSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)) };
if (!RtlGetVersion(ref osVersionInfo))
{
  // TODO: Error handling, call GetVersionEx, etc.
}
Wheelock answered 4/4, 2018 at 0:45 Comment(2)
RtlGetVersion returns NTSTATUS not bool, and returns zero for success. If you use the interop as is then the function returns false on success.Eucharis
there are actually two issues: 1) it is not BOOL but NTSTATUS and 2) you have to add "CharSet = CharSet.Unicode" in the DllImport section. The latter won't affect the version numbers, but some other properties (e.g. ProductType) are not correctly filled. Based on the answer here, I created a GitHub project to properly detect all Windows versions in C#: github.com/pruggitorg/detect-windows-versionKristof
P
1

You can read from registry through code and do specific action what you intended.

Say for example:

Registry key is located at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion and then look for "ProductName".

You can open registry information by giving regedit.exe in run (Windows+r)

var reg              =Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\WindowsNT\CurrentVersion");

        string productName = (string)reg.GetValue("ProductName");

        if (productName.StartsWith("Windows 10"))
        {
        }
        else
        {
        }
Ptolemaist answered 9/2, 2017 at 13:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.