C++ How to detect Windows Server 2019?

12

Microsoft released Windows Server 2019 on October 2, 2018. From Windows 2000 and up until this Windows version, you could call a WinAPI function GetVersionEx with a struct OSVERSIONINFOEX and depending on the variables of dwMajorVersion, dwMinorVersion and wProductType determine Windows version, for example, Windows 8.1, Windows 10, Windows Server 2012 R2. The code everyone used was something like this:

OSVERSIONINFOEX osvi;
SecureZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (GetVersionEx(&osvi)) {
    if (osvi.dwMajorVersion == 10 &&
        osvi.dwMinorVersion == 0 &&
        osvi.wProductType != VER_NT_WORKSTATION) {
            Console->Log("We are running on Windows Server 2016");
        }
}

Judging from Wikipedia the Windows Server 2019 has the same version number of NT 10.0 as Server 2016. So the above code does not work anymore.

Also, Microsoft Docs contains the following note: GetVersionEx may be altered or unavailable for releases after Windows 8.1. Instead, use the Version Helper functions.

Unfortunately, the Version Helper functions does not have a function to detect Server 2019. Also, the strange thing is that Docs page about Targeting stops at the Windows 10, and does not talk about Server editions, while these Targeting manifests is mandatory for detecting OS above Windows 8.1 or Server 2012.

Update 1. As @IInspectable and @RbMm commented about usage of RtlGetVersion function. So I ran the following code (taken from this answer):

typedef LONG NTSTATUS, *PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)

typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);

RTL_OSVERSIONINFOW GetRealOSVersion() {
    HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
    if (hMod) {
        RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
        if (fxPtr != nullptr) {
            RTL_OSVERSIONINFOW rovi = { 0 };
            rovi.dwOSVersionInfoSize = sizeof(rovi);
            if ( STATUS_SUCCESS == fxPtr(&rovi) ) {
                return rovi;
            }
        }
    }
    RTL_OSVERSIONINFOW rovi = { 0 };
    return rovi;
}

And here are the results for Windows 10:

  • dwMajorVersion = 10
  • dwMinorVersion = 0
  • dwBuildNumber = 17134
  • dwPlatformId = 2

Windows Server 2019:

  • dwMajorVersion = 10
  • dwMinorVersion = 0
  • dwBuildNumber = 17763
  • dwPlatformId = 2

Update2. As requested, posting full info from OSVERSIONINFOEX struct that was obtained via GetVersionEx call with a manifest file containing all the Targets till Windows 10 (see the Targeting link above):

// Windows 10
osvi.dwOSVersionInfoSize = 284
osvi.dwMajorVersion = 10
osvi.dwMinorVersion = 0
osvi.dwBuildNumber = 17134
osvi.dwPlatformId = 2
osvi.szCSDVersion =
osvi.wServicePackMinor = 0
osvi.wServicePackMinor = 0
osvi.wSuiteMask = 256  // 0x100
osvi.wProductType = 1
osvi.wReserved = 0

// Windows Server 2016
osvi.dwOSVersionInfoSize = 284
osvi.dwMajorVersion = 10
osvi.dwMinorVersion = 0
osvi.dwBuildNumber = 14393
osvi.dwPlatformId = 2
osvi.szCSDVersion =
osvi.wServicePackMinor = 0
osvi.wServicePackMinor = 0
osvi.wSuiteMask = 400
osvi.wProductType = 3
osvi.wReserved = 0

// Windows Server 2019
osvi.dwOSVersionInfoSize = 284
osvi.dwMajorVersion = 10
osvi.dwMinorVersion = 0
osvi.dwBuildNumber = 17763
osvi.dwPlatformId = 2
osvi.szCSDVersion =
osvi.wServicePackMinor = 0
osvi.wServicePackMinor = 0
osvi.wSuiteMask = 400  // 0x190
osvi.wProductType = 3
osvi.wReserved = 0

Update 3. Calling RtlGetVersion with a struct RTL_OSVERSIONINFOEXW we get exactly the same result as in Update 2.

c++
windows
winapi
asked on Stack Overflow Nov 20, 2018 by Maris B. • edited Nov 21, 2018 by Maris B.

5 Answers

1

It's the same story for each new Windows version. And again with Windows 2019

You should use VerifyVersionInfoW ... BUT also update your program manifest.

The questions is : What is the manifest for the new Windows version ...

Windows 2016 was linked with Windows 10 with id :

see here : https://docs.microsoft.com/en-us/windows/desktop/sbscs/application-manifests

answered on Stack Overflow Dec 7, 2018 by Herve K.
1

According to discussions in Windows Server 2019 version info:

[Windows] Server 2019 Datacenter Edition build 17744, the ReleaseId field shows 1809.

So something like this should do the trick:

const auto isWinServer2019Plus =
  IsWindowsServer() &&
  IsWindowsVersionOrGreater(10, 0, 1803);
answered on Stack Overflow Apr 3, 2019 by NuSkooler • edited Apr 6, 2019 by NuSkooler
1

Apart from checking MajorVersion, MinorVersion and ProductType you also have to check the ReleaseId or BuildNumber.

ReleaseId: 1809 and BuildNumber: 17763 are associated with the release versions of Windows Server 2019 and Windows Server, version 1809. So by checking for those numbers you should at least be sure that you're either dealing with Windows Server 2019 or Windows Server, version 1809 (Semi-Annual Channel) (Datacenter Core, Standard Core).

enter image description here

See: Windows Server release information

Note: Insider Preview builds of Windows Server 2019 can have ReleaseId 1803 or BuildNumbers below 17763.


In this thread Mary Hoffman from Microsoft says:

ReleaseId

1809 is associated with Windows Server 2019 and Windows Server, version 1809 only. (post)

Windows Server 2016 will always be 1607. Once a product is released, that ID will not change. (post)

Following that logic Windows Server 2019 will also always be 1809.

You can read the ReleaseId from this registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion - ReleaseId

See: How to read a value from the Windows registry

BuildNumber

Windows Server 2019 stops at 17763. That was the final major build number.

Anything higher than that (>=17764) will be vNext builds. (post)

Windows Server 2016 will always be 10.0.14393.### where ### increments as cumulative updates are installed.

Windows Server 2019 will always be 10.0.17763.### where ### increments as cumulative updates are installed. (post)

So BuildNumber 17763 should always correspond to Window Server 2019 or Windows Server, version 1809 (or Windows 10 1809, but checking ProductType tells you if its a server).

answered on Stack Overflow Apr 14, 2020 by frido • edited Sep 24, 2020 by frido
0

The best way I found is to use the GetVersionEx method you mentioned, if that returns 6.2 (which it will for Windows 8.1 and above), fall back to the wmic api.
Code below taken from Microsoft to get the OS name using wmic api.
Reference: https://docs.microsoft.com/en-us/windows/win32/wmisdk/example--getting-wmi-data-from-the-local-computer

#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>

#pragma comment(lib, "wbemuuid.lib")

int main(int argc, char **argv)
{
HRESULT hres;

// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------

hres =  CoInitializeEx(0, COINIT_MULTITHREADED); 
if (FAILED(hres))
{
    cout << "Failed to initialize COM library. Error code = 0x" 
        << hex << hres << endl;
    return 1;                  // Program has failed.
}

// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------

hres =  CoInitializeSecurity(
    NULL, 
    -1,                          // COM authentication
    NULL,                        // Authentication services
    NULL,                        // Reserved
    RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
    RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
    NULL,                        // Authentication info
    EOAC_NONE,                   // Additional capabilities 
    NULL                         // Reserved
    );


if (FAILED(hres))
{
    cout << "Failed to initialize security. Error code = 0x" 
        << hex << hres << endl;
    CoUninitialize();
    return 1;                    // Program has failed.
}

// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------

IWbemLocator *pLoc = NULL;

hres = CoCreateInstance(
    CLSID_WbemLocator,             
    0, 
    CLSCTX_INPROC_SERVER, 
    IID_IWbemLocator, (LPVOID *) &pLoc);

if (FAILED(hres))
{
    cout << "Failed to create IWbemLocator object."
        << " Err code = 0x"
        << hex << hres << endl;
    CoUninitialize();
    return 1;                 // Program has failed.
}

// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method

IWbemServices *pSvc = NULL;

// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
     _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
     NULL,                    // User name. NULL = current user
     NULL,                    // User password. NULL = current
     0,                       // Locale. NULL indicates current
     NULL,                    // Security flags.
     0,                       // Authority (for example, Kerberos)
     0,                       // Context object 
     &pSvc                    // pointer to IWbemServices proxy
     );

if (FAILED(hres))
{
    cout << "Could not connect. Error code = 0x" 
         << hex << hres << endl;
    pLoc->Release();     
    CoUninitialize();
    return 1;                // Program has failed.
}

cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;


// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------

hres = CoSetProxyBlanket(
   pSvc,                        // Indicates the proxy to set
   RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
   RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
   NULL,                        // Server principal name 
   RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
   RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
   NULL,                        // client identity
   EOAC_NONE                    // proxy capabilities 
);

if (FAILED(hres))
{
    cout << "Could not set proxy blanket. Error code = 0x" 
        << hex << hres << endl;
    pSvc->Release();
    pLoc->Release();     
    CoUninitialize();
    return 1;               // Program has failed.
}

// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----

// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
    bstr_t("WQL"), 
    bstr_t("SELECT * FROM Win32_OperatingSystem"),
    WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 
    NULL,
    &pEnumerator);

if (FAILED(hres))
{
    cout << "Query for operating system name failed."
        << " Error code = 0x" 
        << hex << hres << endl;
    pSvc->Release();
    pLoc->Release();
    CoUninitialize();
    return 1;               // Program has failed.
}

// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------

IWbemClassObject *pclsObj = NULL;
ULONG uReturn = 0;

while (pEnumerator)
{
    HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, 
        &pclsObj, &uReturn);

    if(0 == uReturn)
    {
        break;
    }

    VARIANT vtProp;

    // Get the value of the Name property
    hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
    wcout << " OS Name : " << vtProp.bstrVal << endl;
    VariantClear(&vtProp);

    pclsObj->Release();
}

// Cleanup
// ========

pSvc->Release();
pLoc->Release();
pEnumerator->Release();
CoUninitialize();

return 0;   // Program successfully completed.

}
answered on Stack Overflow Feb 27, 2020 by Mayank R
0

The only difference is dwBuildNumber. So this code worked for me:

else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0) 
{
    if (osvi.wProductType == VER_NT_WORKSTATION)
        m_csOsType += _T("10 ");
    else
    {
        if (osvi.dwBuildNumber >= 17763)
            m_csOsType += _T("Server 2019 ");
        else
            m_csOsType += _T("Server 2016 ");
    }
        
}
answered on Stack Overflow Jul 31, 2020 by Oleksii Aleksandrov • edited Jul 31, 2020 by Suit Boy Apps

User contributions licensed under CC BY-SA 3.0