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

如何开发支持实例方法调用的C++解耦WMI非静态方法提供程序

C++解耦WMI提供程序实例方法实现问题

我正在开发一款C++解耦WMI提供程序,目前仅能调用静态方法,需要实现实例方法的调用。代码基于Windows经典的实例提供程序和方法提供程序模板。

静态方法调用(正常执行)

PS D:\projects\apps\instance_provider2> Invoke-CimMethod -ClassName MethProvSamp -MethodName Echo -Namespace root/mycim

ReturnValue sOutArg PSComputerName
----------- ------- --------------
          7 default

实例方法调用(报错)

PS D:\projects\apps\instance_provider2> (Get-WmiObject -Namespace "root\mycim" -ClassName "HwSetting").SetHw()
Exception calling "SetHw" with "0" argument(s): "Object reference not set to an instance of an object."
At line:1 char:1
+ (Get-WmiObject -Namespace "root\mycim" -ClassName "HwSetting").SetHw()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : CatchFromBaseAdapterMethodInvoke

MOF定义

[dynamic: ToInstance, singleton, Provider("DecoupledInstanceProvider2") ]
class HwSetting
{
    [implemented]
    uint32 SetHw();
};

[dynamic: ToInstance, provider("DecoupledInstanceProvider2")]
class MethProvSamp
{
     [implemented, static]
     uint32 Echo([in]string sInArg="default", [out] string sOutArg);
};

核心代码

/******************************************************************************
 *
 *  Name:   ExecMethodAsync
 *
 *  Description:
 *          Allow provider to execute a method.
 *
 *****************************************************************************/

HRESULT CProvider_IWbemServices :: ExecMethodAsync ( 

    const BSTR a_ObjectPath ,
    const BSTR a_MethodName ,
    long a_Flags ,
    IWbemContext *a_Context ,
    IWbemClassObject *a_InParams ,
    IWbemObjectSink *a_Sink
) 
{
    HRESULT hr = S_OK;

    printf("%s:%d\n", __FUNCTION__, __LINE__);
    printf("a_ObjectPath:%S\n", a_ObjectPath);
    printf("a_MethodName:%S\n", a_MethodName);

    if (_wcsicmp(a_MethodName, L"Echo") == 0) {

        IWbemClassObject* pClass = NULL;
        IWbemClassObject* pOutClass = NULL;
        IWbemClassObject* pOutParams = NULL;

        hr = CoImpersonateClient();
        if (FAILED(hr))
        {
            a_Sink->SetStatus(0, hr, NULL, NULL);
            return hr;
        }

        DWORD t_CurrentImpersonationLevel = GetCurrentImpersonationLevel();
        if (t_CurrentImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE)
        {
            CoRevertToSelf();
            hr = WBEM_E_ACCESS_DENIED;
            a_Sink->SetStatus(0, hr, NULL, NULL);
            return hr;
        }

        BSTR ClassName = SysAllocString(L"MethProvSamp");
        if (!ClassName)
        {
            CoRevertToSelf();
            a_Sink->SetStatus(0, WBEM_E_OUT_OF_MEMORY, NULL, NULL);
            return hr;
        }

        BSTR InputArgName = SysAllocString(L"sInArg");
        if (!InputArgName)
        {
            SysFreeString(ClassName);
            CoRevertToSelf();
            a_Sink->SetStatus(0, WBEM_E_OUT_OF_MEMORY, NULL, NULL);
            return hr;
        }

        BSTR OutputArgName = SysAllocString(L"sOutArg");
        if (!OutputArgName)
        {
            SysFreeString(ClassName);
            SysFreeString(InputArgName);
            CoRevertToSelf();
            a_Sink->SetStatus(0, WBEM_E_OUT_OF_MEMORY, NULL, NULL);
            return hr;
        }
        BSTR retValName = SysAllocString(L"ReturnValue");
        if (!retValName)
        {
            SysFreeString(ClassName);
            SysFreeString(InputArgName);
            SysFreeString(OutputArgName);
            CoRevertToSelf();
            a_Sink->SetStatus(0, WBEM_E_OUT_OF_MEMORY, NULL, NULL);
            return hr;
        }

        hr = m_CoreService->GetObject(ClassName, 0, a_Context, &pClass, NULL);
        if (hr != S_OK)
        {
            a_Sink->SetStatus(0, hr, NULL, NULL);
            return WBEM_S_NO_ERROR;
        }

        hr = pClass->GetMethod(a_MethodName, 0, NULL, &pOutClass);
        pOutClass->SpawnInstance(0, &pOutParams);

        VARIANT var;
        VariantInit(&var);
        a_InParams->Get(InputArgName, 0, &var, NULL, NULL);
        pOutParams->Put(OutputArgName, 0, &var, 0);
        
        long lLen = (long)wcslen(var.bstrVal);
        VariantClear(&var);
        var.vt = VT_I4;
        var.lVal = lLen;
        pOutParams->Put(retValName, 0, &var, 0);

        hr = a_Sink->Indicate(1, &pOutParams);
        pOutParams->Release();
        pOutClass->Release();
        pClass->Release();
        SysFreeString(ClassName);
        SysFreeString(InputArgName);
        SysFreeString(OutputArgName);
        SysFreeString(retValName);
    }
    else if (_wcsicmp(a_MethodName, L"SetHw") == 0) {
        printf("SetHw method invoked\n");

        if (m_HwSetting_Object == NULL) {
            printf("m_HwSetting_Object is NULL\n");
            a_Sink->SetStatus(0, WBEM_E_FAILED, NULL, NULL);
            return WBEM_E_FAILED;
        }

        IWbemClassObject* pOutParams = NULL;
        hr = m_HwSetting_Object->GetMethod(a_MethodName, 0, NULL, &pOutParams);
        if (SUCCEEDED(hr)) {
            VARIANT var;
            VariantInit(&var);
            var.vt = VT_I4;
            var.lVal = 0;
            hr = pOutParams->Put(L"ReturnValue", 0, &var, 0);

            if (SUCCEEDED(hr)) {
                hr = a_Sink->Indicate(1, &pOutParams);
            }

            pOutParams->Release();
        }
        else {
            printf("Failed to get method: %S\n", a_MethodName);
        }
    } else {
        return WBEM_E_INVALID_PARAMETER;
    }

    hr = a_Sink->SetStatus(0, WBEM_S_NO_ERROR, NULL, NULL);
    return WBEM_S_NO_ERROR;
}


HRESULT CProvider_IWbemServices :: CreateInstanceEnumAsync (

    const BSTR a_Class ,
    long a_Flags ,
    IWbemContext *a_Context ,
    IWbemObjectSink *a_Sink
) 
{
    printf("%s:%d %S\n", __FUNCTION__, __LINE__, a_Class);
    HRESULT hr = S_OK ;

    if ( _wcsicmp ( a_Class , L"ContactInfo" ) == 0 ) 
    {
        PSECURITY_DESCRIPTOR secDescriptor = NULL;
        BOOL bRes = ConvertStringSecurityDescriptorToSecurityDescriptor
            ( L"O:BAG:BAD:(A;;0x10000001;;;BA)(A;;0x10000001;;;SY)(A;;0x10000001;;;LA)(A;;0x10000001;;;SY)(A;;0x10000001;;;S-1-5-20)(A;;0x10000001;;;S-1-5-19)",
            SDDL_REVISION_1,
            (PSECURITY_DESCRIPTOR *) &secDescriptor,
            NULL);

        if (! bRes)
        {
            hr = WBEM_E_ACCESS_DENIED;
            a_Sink->SetStatus ( 0 , hr , NULL , NULL ) ;
            return hr ;
        }

        hr = CoImpersonateClient () ;
        if ( FAILED ( hr ) )
        {
            LocalFree(secDescriptor);
            a_Sink->SetStatus ( 0 , hr , NULL , NULL ) ;
            return hr ;
        }   

        hr = CheckAccess((SECURITY_DESCRIPTOR *)secDescriptor, 
                            MASK_CLIENT_ACCESS_BIND,
                            &s_ClientAccessMapping);

        LocalFree(secDescriptor);
        CoRevertToSelf () ;

        if (FAILED(hr))
        {
            hr = WBEM_E_ACCESS_DENIED;
            a_Sink->SetStatus ( 0 , hr , NULL , NULL ) ;
            return hr ;
        }       

        hr = CreateInstanceEnumAsync_Contacts ( 
            m_ContactInfo_Object ,
            a_Flags ,
            a_Context , 
            a_Sink
        ) ;

    }
    else if (_wcsicmp(a_Class, L"HwSetting") == 0)
    {
        IWbemClassObject* pClassObject = NULL;
        hr = m_CoreService->GetObject(a_Class, 0, a_Context, &pClassObject, NULL);
        if (SUCCEEDED(hr))
        {
            hr = CreateInstanceEnumAsync_HwSetting(pClassObject, a_Flags, a_Context, a_Sink);
            pClassObject->Release();
        }
    }
    else
    {
        hr = WBEM_E_INVALID_CLASS ;
    }

    a_Sink->SetStatus ( 0 , hr , NULL , NULL ) ;
    return hr ;
}

问题分析与解决步骤

1. 根本原因

报错提示对象引用为空,结合代码中m_HwSetting_Object == NULL的打印信息,说明:

  • 单例实例m_HwSetting_Object未正确初始化
  • 实例方法的处理逻辑错误(直接从实例对象获取方法模板,而非类对象)
  • 实例枚举逻辑可能未正确返回实例给客户端

2. 具体修复方案

(1)初始化单例实例

在提供程序的初始化方法中创建并缓存HwSetting单例实例:

HRESULT CProvider::Initialize(IWbemServices* pCoreService, ...) {
    HRESULT hr = S_OK;
    m_CoreService = pCoreService;
    m_CoreService->AddRef();

    // 创建HwSetting单例实例
    IWbemClassObject* pHwClass = NULL;
    hr = m_CoreService->GetObject(L"HwSetting", 0, NULL, &pHwClass, NULL);
    if (SUCCEEDED(hr)) {
        hr = pHwClass->SpawnInstance(0, &m_HwSetting_Object);
        pHwClass->Release();
    }

    return hr;
}

// 提供程序销毁时释放实例
void CProvider::Terminate() {
    if (m_HwSetting_Object != NULL) {
        m_HwSetting_Object->Release();
        m_HwSetting_Object = NULL;
    }
    // ... 其他资源释放
}
(2)修正实例方法处理逻辑

GetMethod需从类对象获取,而非实例对象,修改ExecMethodAsync中SetHw的处理分支:

else if (_wcsicmp(a_MethodName, L"SetHw") == 0) {
    printf("SetHw method invoked\n");

    // 获取HwSetting类对象
    IWbemClassObject* pClass = NULL;
    HRESULT hr = m_CoreService->GetObject(L"HwSetting", 0, a_Context, &pClass, NULL);
    if (FAILED(hr)) {
        a_Sink->SetStatus(0, hr, NULL, NULL);
        return hr;
    }

    // 获取方法输出参数模板
    IWbemClassObject* pOutClass = NULL;
    hr = pClass->GetMethod(a_MethodName, 0, NULL, &pOutClass);
    pClass->Release();
    if (FAILED(hr)) {
        printf("Failed to get method: %S\n", a_MethodName);
        a_Sink->SetStatus(0, hr, NULL, NULL);
        return hr;
    }

    // 生成输出参数实例
    IWbemClassObject* pOutParams = NULL;
    hr = pOutClass->SpawnInstance(0, &pOutParams);
    pOutClass->Release();
    if (FAILED(hr)) {
        a_Sink->SetStatus(0, hr, NULL, NULL);
        return hr;
    }

    // 设置返回值(可根据业务逻辑修改)
    VARIANT var;
    VariantInit(&var);
    var.vt = VT_I4;
    var.lVal = 0;
    hr = pOutParams->Put(L"ReturnValue", 0, &var, 0);

    if (SUCCEEDED(hr)) {
        hr = a_Sink->Indicate(1, &pOutParams);
    }

    pOutParams->Release();
}
(3)确保实例枚举返回正确实例

检查CreateInstanceEnumAsync_HwSetting函数,确保返回已初始化的单例实例:

HRESULT CProvider_IWbemServices::CreateInstanceEnumAsync_HwSetting(IWbemClassObject* pClassObject, long a_Flags, IWbemContext* a_Context, IWbemObjectSink* a_Sink) {
    HRESULT hr = S_OK;
    if (m_HwSetting_Object != NULL) {
        // 将单例实例返回给WMI客户端
        hr = a_Sink->Indicate(1, &m_HwSetting_Object);
    } else {
        hr = WBEM_E_FAILED;
    }
    return hr;
}
(4)可选:增强实例路径验证

对于实例方法,WMI会传入a_ObjectPath参数,可解析路径验证目标实例,提升健壮性:

// 在SetHw分支开头添加路径验证
IWbemPath* pPath = NULL;
HRESULT hr = CoCreateInstance(CLSID_WbemPath, NULL, CLSCTX_INPROC_SERVER, IID_IWbemPath, (void**)&pPath);
if (SUCCEEDED(hr)) {
    hr = pPath->SetText(a_ObjectPath);
    if (SUCCEEDED(hr)) {
        BSTR className = pPath->GetClassName();
        if (_wcsicmp(className, L"HwSetting") != 0) {
            SysFreeString(className);
            pPath->Release();
            return WBEM_E_INVALID_CLASS;
        }
        SysFreeString(className);
    }
    pPath->Release();
}

3. 验证测试

重新编译、注册提供程序后,执行以下命令测试:

(Get-WmiObject -Namespace "root\mycim" -ClassName "HwSetting").SetHw()

应正常返回ReturnValue为0的结果。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 08:52:01