如何开发支持实例方法调用的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
相关产品推荐
相关产品推荐

