You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Visual 6.0 C++调用WMI获取Win32_OperatingSystem枚举属性时遇0x80041018错误

WMI调用Next函数返回0x80041018错误的解决办法

问题场景

在Windows XP系统下使用Visual C++ 6.0开发,尝试通过WMI枚举Win32_OperatingSystem类属性时,调用IEnumWbemClassObject::Next函数返回错误代码0x80041018,原代码缺少关键错误处理和规范的COM参数使用:

int CSystemInfo::Init()
{
    HRESULT hRes = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
    if(FAILED(hRes))
        return 1;

    hRes = CoInitializeSecurity(NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_DEFAULT,
        RPC_C_IMP_LEVEL_DELEGATE,
        NULL,
        EOAC_NONE,
        NULL
        );
  
    IWbemLocator *locator = NULL;

    hRes = CoCreateInstance(
        CLSID_WbemLocator,
        0,
        CLSCTX_INPROC_SERVER,
        IID_IWbemLocator,
        (LPVOID*)&locator
        ); 

    IWbemServices *services = NULL;
    hRes = locator->ConnectServer(
        BSTR(L"ROOT\\CIMV2"),
        NULL,
        NULL,
        0,
        NULL,
        0,
        0,
        &services);
   
    hRes = CoSetProxyBlanket(services,
        RPC_C_AUTHN_WINNT,
        RPC_C_AUTHZ_NONE,
        NULL,
        RPC_C_AUTHN_LEVEL_CALL,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        0,
        EOAC_NONE
        );

    IEnumWbemClassObject * pEnumerator = NULL;
    hRes = services->ExecQuery(BSTR("WQL"),
        BSTR("SELECT * FROM Win32_OperatingSystem"),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &pEnumerator);

    IWbemClassObject *obj=NULL;
    ULONG ret_objs_num;

    hRes = WBEM_S_NO_ERROR;
    while(WBEM_S_NO_ERROR == hRes)
    {
        //ERROR 0x80040018
        hRes = pEnumerator->Next(WBEM_INFINITE, 1, &obj, &ret_objs_num);

        if(FAILED(hRes) || obj== NULL || ret_objs_num==0)
           break;        
    }

    return 0;
}

错误原因分析

  1. BSTR参数不合法:直接用BSTR()强制转换字符串字面量,VC6中这种方式无法生成符合WMI要求的有效BSTR,导致查询语法被判定为无效(对应错误码0x80041018即WBEM_E_INVALID_SYNTAX)。
  2. 缺失关键错误检查:CoInitializeSecurity、CoCreateInstance、ConnectServer、ExecQuery等步骤的返回值未做失败判断,若前面步骤已出错,后续Next调用必然失败。
  3. 安全权限不匹配:CoInitializeSecurity中使用RPC_C_IMP_LEVEL_DELEGATE,Windows XP环境下普通进程无法获取该权限,应使用更宽松的RPC_C_IMP_LEVEL_IMPERSONATE。
  4. COM对象未释放:代码中未调用Release()释放所有COM指针,不仅造成内存泄漏,还可能破坏WMI的内部状态。

解决办法及修正代码

核心修复点

  • 使用SysAllocString创建合法BSTR,避免强制转换。
  • 为每个COM调用添加严格的错误检查,提前终止错误流程。
  • 调整CoInitializeSecurity的 impersonation level 为RPC_C_IMP_LEVEL_IMPERSONATE。
  • 正确释放所有COM对象,清理资源。

修正后的代码

#include <wbemcli.h>
#include <comutil.h>
#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "comsupp.lib")

int CSystemInfo::Init()
{
    HRESULT hRes = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if (FAILED(hRes))
    {
        return 1;
    }

    // 调整安全设置为XP兼容的 impersonation level
    hRes = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_DEFAULT,
        RPC_C_IMP_LEVEL_IMPERSONATE, // 替换原有的DELEGATE
        NULL,
        EOAC_NONE,
        NULL
    );
    // 忽略RPC_E_TOO_LATE错误(若已初始化安全设置)
    if (FAILED(hRes) && hRes != RPC_E_TOO_LATE)
    {
        CoUninitialize();
        return 2;
    }

    IWbemLocator* locator = NULL;
    hRes = CoCreateInstance(
        CLSID_WbemLocator,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IWbemLocator,
        (LPVOID*)&locator
    );
    if (FAILED(hRes))
    {
        CoUninitialize();
        return 3;
    }

    IWbemServices* services = NULL;
    BSTR strNamespace = SysAllocString(L"ROOT\\CIMV2");
    hRes = locator->ConnectServer(
        strNamespace,
        NULL,
        NULL,
        NULL,
        0,
        NULL,
        NULL,
        &services
    );
    SysFreeString(strNamespace);
    if (FAILED(hRes))
    {
        locator->Release();
        CoUninitialize();
        return 4;
    }

    // 设置代理 blanket
    hRes = CoSetProxyBlanket(
        services,
        RPC_C_AUTHN_WINNT,
        RPC_C_AUTHZ_NONE,
        NULL,
        RPC_C_AUTHN_LEVEL_CALL,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE
    );
    if (FAILED(hRes))
    {
        services->Release();
        locator->Release();
        CoUninitialize();
        return 5;
    }

    IEnumWbemClassObject* pEnumerator = NULL;
    BSTR strWQL = SysAllocString(L"WQL");
    BSTR strQuery = SysAllocString(L"SELECT * FROM Win32_OperatingSystem");
    hRes = services->ExecQuery(
        strWQL,
        strQuery,
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &pEnumerator
    );
    SysFreeString(strWQL);
    SysFreeString(strQuery);
    if (FAILED(hRes))
    {
        services->Release();
        locator->Release();
        CoUninitialize();
        return 6;
    }

    IWbemClassObject* obj = NULL;
    ULONG ret_objs_num = 0;
    hRes = WBEM_S_NO_ERROR;

    while (hRes == WBEM_S_NO_ERROR)
    {
        hRes = pEnumerator->Next(WBEM_INFINITE, 1, &obj, &ret_objs_num);
        if (FAILED(hRes) || ret_objs_num == 0 || obj == NULL)
        {
            break;
        }

        // 这里可以添加属性读取逻辑,例如获取系统版本
        VARIANT vtProp;
        VariantInit(&vtProp);
        if (SUCCEEDED(obj->Get(L"Caption", 0, &vtProp, NULL, NULL)))
        {
            // 处理vtProp中的数据,例如输出到日志
            VariantClear(&vtProp);
        }

        obj->Release();
        obj = NULL;
    }

    // 释放所有COM对象
    if (pEnumerator) pEnumerator->Release();
    if (services) services->Release();
    if (locator) locator->Release();
    CoUninitialize();

    return 0;
}

额外说明

  • 确保项目链接wbemuuid.lib和comsupp.lib(VC6中需手动添加)。
  • CoInitializeSecurity可能返回RPC_E_TOO_LATE,这是正常现象(若进程已初始化安全设置),可忽略该错误。
  • 读取WMI属性时需正确初始化和清理VARIANT对象,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Bear WiliBear

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:35:09