C ++如何检测Windows Server 2019?

时间:2018-11-20 12:37:12

标签: c++ windows winapi

Microsoft于2018年10月2日发布了Windows Server2019。从Windows 2000直到此Windows版本,您可以使用结构GetVersionEx并根据{的变量来调用WinAPI函数OSVERSIONINFOEX {1}},dwMajorVersiondwMinorVersion确定Windows版本,例如Windows 8.1,Windows 10,Windows Server 2012 R2。每个人使用的代码是这样的:

wProductType

Wikipedia来看,Windows Server 2019具有与Server 2016相同的版本号NT 10.0。因此上述代码不再起作用。

此外,Microsoft Docs包含以下注释: GetVersionEx可能已更改或在Windows 8.1之后的版本中不可用。而是使用版本帮助器功能。

不幸的是,Version Helper functions没有检测Server 2019的功能。而且,奇怪的是,关于Targeting的文档页面在Windows 10上停止运行,而没有谈论服务器版本,这些目标清单对于检测Windows 8.1或Server 2012以上的操作系统是必不可少的。

更新1。 正如@IInspectable和@RbMm评论了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"); } } 函数的用法。因此,我运行了以下代码(摘自this answer):

RtlGetVersion

这是 Windows 10的结果:

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

Windows Server 2019:

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

Update2。。根据要求,发布通过 GetVersionEx 调用从 OSVERSIONINFOEX 结构获得的完整信息,并包含一个清单文件,其中包含所有目标。 Windows 10(请参见上面的“定向”链接):

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;
}

更新3。使用结构// 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 调用RtlGetVersion会得到与更新2完全相同的结果。

5 个答案:

答案 0 :(得分:1)

除了检查MajorVersionMinorVersionProductType,您还必须检查ReleaseIdBuildNumber

ReleaseId: 1809BuildNumber: 17763 Windows Server 2019 Windows Server 1809版的发行版相关联>。因此,通过检查这些数字,您至少应确保自己正在处理 Windows Server 2019 Windows Server版本1809(半年通道)(数据中心核心,标准核心)

enter image description here

请参阅:Windows Server release information

注意: Windows Server 2019的Insider Preview版本可以使ReleaseId 1803或BuildNumbers低于17763。


Microsoft的this thread中的玛丽·霍夫曼说:

ReleaseId

1809与Windows Server 2019和Windows Server相关联, 仅版本1809。 (post)

Windows Server 2016始终为1607。发布产品后, 该ID不会更改。 (post)

遵循该逻辑,Windows Server 2019也将始终为1809

您可以从以下注册表项中读取ReleaseId:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion - ReleaseId

请参阅:How to read a value from the Windows registry

内部编号

Windows Server 2019停止在17763。这是最终的主要版本 数字。

vNext版本高于(> = 17764)的任何内容。 (post)

Windows Server 2016始终为10.0.14393。###,其中###递增 安装了累积更新。

Windows Server 2019始终为10.0.17763。###,其中###递增 因为安装了累积更新。 (post)

因此,BuildNumber 17763应该始终与 Window Server 2019 Windows Server版本1809 (或Windows 10 1809)相对应,但是检查ProductType会告诉您您是服务器)。

答案 1 :(得分:0)

每个新的Windows版本都是相同的故事。再次使用Windows 2019

您应该使用VerifyVersionInfoW ...但同时也会更新程序清单。

问题是:新的Windows版本的清单是什么...

Windows 2016与Windows 10链接的ID为:

     

请参阅此处:https://docs.microsoft.com/en-us/windows/desktop/sbscs/application-manifests

答案 2 :(得分:0)

根据Windows Server 2019 version info中的讨论:

  

[Windows] Server 2019数据中心版内部版本17744,ReleaseId字段显示1809。

所以这样的事情应该可以解决问题:

const auto isWinServer2019Plus =
  IsWindowsServer() &&
  IsWindowsVersionOrGreater(10, 0, 1803);

答案 3 :(得分:0)

我发现的最好方法是使用您提到的GetVersionEx方法,如果该方法返回6.2(对于Windows 8.1及更高版本,它将返回到wmic api)。
下面的代码来自Microsoft,用于使用wmic api获取操作系统名称。
参考: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.

}

答案 4 :(得分:0)

唯一的区别是dwBuildNumber。所以这段代码对我有用:

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 ");
    }
        
}