WMI:从 Win32_OperatingSystem table 获取奇怪的值

WMI: Getting weird value from Win32_OperatingSystem table

我在使用 WMI 和 C++ 时遇到问题。

我是第一次用,所以就拿了基础的documentation example。但是 运行 它对我不起作用。

我试图从 Win32_OperatingSystem 中提取 OS 名称,但是当我显示它时,我得到了 0x9a2fec

然后我尝试使用 MaxNumberOfProcesses,结果是“-1”。

我在 Windows 10,我不知道发生了什么。

我的代码:

#include <iostream>
#define _WIN32_DCOM
#include <windows.h>
#include <Wbemidl.h>
#include <comdef.h>

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

bool initializeCom(){
    // Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------

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

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

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

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

bool setUpWBEM(IWbemLocator*& wbemLocator, IWbemServices*& wbemServices){
    // Step 3: ---------------------------------------------------
    // Obtain the initial locator to WMI -------------------------
    HRESULT hres = CoCreateInstance(
        CLSID_WbemLocator,             
        0, 
        CLSCTX_INPROC_SERVER, 
        IID_IWbemLocator, (LPVOID *) &wbemLocator);

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

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

    // Connect to the root\cimv2 namespace with
    // the current user and obtain pointer wbemServices
    // to make IWbemServices calls.

    hres = wbemLocator->ConnectServer(
         _bstr_t(L"ROOT\CIMV2"), // Object path of WMI namespace
         nullptr,                    // User name. NULL = current user
         nullptr,                    // User password. NULL = current
         0,                       // Locale. NULL indicates current
         0,                    // Security flags.
         0,                       // Authority (for example, Kerberos)
         0,                       // Context object 
        &wbemServices            // pointer to IWbemServices proxy
         );

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

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


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

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

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

    return true;
}

int main() {

    std::cout << "HelloWorld" << std::endl;
    IWbemLocator* wbemLocator{nullptr};
    IWbemServices* wbemServices{nullptr};

    try{
        if(!initializeCom())
            throw "initializeCom failed";

        if(!setUpWBEM(wbemLocator,wbemServices))
            throw "setUpWBEM failed";

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

        BSTR bstr_wql = SysAllocString(L"WQL" );
        BSTR bstr_sql = SysAllocString(L"SELECT * FROM Win32_OperatingSystem" ); 

        // For example, get the name of the operating system
        IEnumWbemClassObject* pEnumerator{nullptr};
        HRESULT hres = wbemServices->ExecQuery(
            bstr_wql, 
            bstr_sql,
            WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 
            nullptr,
            &pEnumerator);
    
        if (FAILED(hres))
        {
            std::cout << "Query for operating system name failed."
                << " Error code = 0x" 
                << std::hex << hres << std::endl;
            wbemServices->Release();
            wbemLocator->Release();
            CoUninitialize();
            throw "ExecQuery failed";;               // Program has failed.
        }

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

        IWbemClassObject *pclsObj{nullptr};
        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);

            if(FAILED(hr))
                std::cout << "Failed to get name " << std::endl;

            std::cout << " OS Name : " << vtProp.bstrVal << std::endl;
            VariantClear(&vtProp);
        
            pclsObj->Release();
        }

        // Cleanup
        // ========
    
        wbemServices->Release();
        wbemLocator->Release();
        pEnumerator->Release();
        CoUninitialize();

    } catch(const std::string& error){
        std::cout << error << std::endl;
    }
    return 0;
}

如果你有什么想法,请告诉我。

问题出在程序的(几乎)最后阶段:显示检索到的“OS 名称”的代码:

std::cout << " OS Name : " << vtProp.bstrVal << std::endl; // Prints pointer address

这里的问题是 BSTR 类型使用 wchar_t* 作为它的缓冲区 1std::cout 流(一个对象std::ostream class) 没有带 wchar_t* 参数的 << 重载(它有一个用于 char*)——所以它使用的版本采用 const void*(作为最佳匹配),打印地址本身,而不是指向的字符串。

对于您的情况,有一个简单的解决方法:使用 std::wcout 流(一个 std::wostream 对象),而不是 2 是否有一个 << 重载需要一个 wchar_t* 参数:

std::wcout << L" OS Name : " << vtProp.bstrVal << std::endl; // Prints the wchar_t* string

只需用上面的内容替换输出行,在我的平台上,将显示的输出从指针值(如您所见)更改为以下内容:

OS Name : Microsoft Windows 10 Pro|C:\WINDOWS|\Device\Harddisk0\Partition3

我不确定是否正是您想要的,但这似乎是一个很大的改进。我相信您可以很容易地添加代码,将该字符串编辑成您想要的格式。


1 请注意,虽然 BSTR 类型被定义为 wchar_t*,但在某些方面它并不是完全相同的东西。有关详细信息,请参阅 this answer

2 在重新阅读您链接的文档时,我看到那里使用了 wcout,用于显示 OS 名称。