我试图了解WMI是如何工作的,但是到目前为止给出的默认示例都很糟糕。
下面是调用Win32_Process
类的Create
方法的示例:
https://learn.microsoft.com/en-us/windows/win32/wmisdk/example--calling-a-provider-method
我已经为此添加了适当的错误处理,我们将每个调用的HRESULT存储在变量hres
中,并检查调用是否失败。比如:
hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
if (FAILED(hres))
{
wprintf("Failed to get class. Error code = 0x%lxn", hres);
return hres;
}
代码执行正确,直到这里:
// Create the values for the in parameters
VARIANT varCommand;
varCommand.vt = VT_BSTR;
varCommand.bstrVal = _bstr_t(L"notepad.exe");
// Store the value for the in parameters
hres = pClassInstance->Put(L"CommandLine", 0,
&varCommand, 0);
wprintf(L"The command is: %sn", V_BSTR(&varCommand));
当pClassInstance->Put
抛出'ol ' c5.
在这一点上,hres
是S_OK
的SpawnInstance
调用,但这些是我们为类实例的指针:
+pClass 0x000001c04e73fca0 IWbemClassObject *
- pClassInstance 0x000001c04e749d60 IWbemClassObject *
- IUnknown {...} IUnknown
- __vfptr 0x00007ff9f8d0ee98 {fastprox.dll!const CWbemInstance::`vftable'{for `_IWmiObject'}} {0x00007ff9f8c6f450 {fastprox.dll!CWbemObject::QueryInterface(void)}, ...} void * *
[0x00000000] 0x00007ff9f8c6f450 {fastprox.dll!CWbemObject::QueryInterface(void)} void *
[0x00000001] 0x00007ff9f8c907d0 {fastprox.dll!CWbemObject::AddRef(void)} void *
[0x00000002] 0x00007ff9f8c8ffd0 {fastprox.dll!CWbemObject::Release(void)} void *
+pInParamsDefinition 0x000001c04e743ca0 IWbemClassObject *
AndvarCommand
:
+varCommand BSTR = 0x000001c04e74ffe8 tagVARIANT
调用栈:
oleaut32.dll!SysAllocString()
vfbasics.dll!AVrfpSysAllocString()
wbemcomn.dll!CVar::SetVariant()
fastprox.dll!CWbemInstance::Put()
> Ele.exe!WMIConnection::InvokeMethod()
所以似乎bstrVal没有被正确设置,我认为?我尝试先用VariantInit
初始化它,我还尝试在堆上动态分配它。
VARIANT varCommand;
VariantInit(&varCommand);
varCommand.vt = VT_BSTR;
varCommand.bstrVal = _bstr_t(L"notepad.exe");
我还尝试手动将变量缓冲区归零,但没有效果。当访问冲突发生时,bstrVal
在内存转储中是这样的:
bstrVal 0x000001c04e74ffe8 <Error reading characters of string.> wchar_t *
<Unable to read memory> wchar_t
这一行:
varCommand.bstrVal = _bstr_t(L"notepad.exe");
代码创建了一个临时_bstr_t
对象,该对象超出了作用域,在分配给varCommand.bstrVal
之后立即销毁分配的BSTR
内存。因此,当将varCommand
传递给pClassInstance->Put()
时,varCommand.bstrVal
将被悬空,指向无效内存。这是未定义行为。
使用这个来保持BSTR
存活,直到你真正使用它:
_bstr_t str(L"notepad.exe");
VARIANT varCommand;
varCommand.vt = VT_BSTR;
varCommand.bstrVal = str;
// use varCommand as needed...
// DO NOT call VarClear() or SysFreeString()!
// You don't own the BSTR memory...
//VarClear(&varCommand);
另外:
VARIANT varCommand;
varCommand.vt = VT_BSTR;
varCommand.bstrVal = SysAllocString(L"notepad.exe");
// use varCommand as needed...
// You DO own the BSTR memory, so free it!
VarClear(&varCommand);
否则,考虑使用_variant_t
代替,让它为您管理内存:
_variant_t varCommand(L"notepad.exe");
hres = pClassInstance->Put(L"CommandLine", 0, &varCommand, 0);
wprintf(L"The command is: %sn", V_BSTR(&varCommand));
我明白了。互联网上有几个论坛帖子询问这个例子,没有给出解决方案,所以我很高兴现在提供这个。
Microsoft的例子使用了不正确的类。
在Microsoft的例子中,他们试图调用Win32_Process
的类实例上的Put
方法来设置参数。
这是不正确的。我们需要通过首先获取Win32_Process::Create
的类方法定义来设置参数,然后在Win32_Process::Create
的新实例中设置其参数。
我们还需要构造一个Win32_ProcessStartup
类对象的实例,因为它是Win32_Process::Create
所需的输入参数。
在下面的例子中,我将填充Win32_ProcessStartup
类实例的一个字段,您可以计算出其余的。
那么这段来自微软例子的代码:
IWbemClassObject* pClass = NULL;
hres = pSvc->GetObject(ClassName, 0, NULL, &pClass, NULL);
IWbemClassObject* pInParamsDefinition = NULL;
hres = pClass->GetMethod(MethodName, 0,
&pInParamsDefinition, NULL);
IWbemClassObject* pClassInstance = NULL;
hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
// Create the values for the in parameters
VARIANT varCommand;
varCommand.vt = VT_BSTR;
varCommand.bstrVal = _bstr_t(L"notepad.exe");
// Store the value for the in parameters
hres = pClassInstance->Put(L"CommandLine", 0,
&varCommand, 0);
wprintf(L"The command is: %sn", V_BSTR(&varCommand));
变成(无错误处理可读性):
// Get the class object
hres = pClass->GetMethod(lpwMethodName, 0,
&pInParamsDefinition, NULL);
hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
// Get the Win32_ProcessStartup class object
IWbemClassObject* pStartupObject = NULL;
hres = pSvc->GetObject(_bstr_t(L"Win32_ProcessStartup"), 0, NULL, &pStartupObject, NULL);
// Create an instance of the Win32_ProcessStartup class object
IWbemClassObject* pStartupInstance = NULL;
hres = pStartupObject->SpawnInstance(0, &pStartupInstance);
// Create the value for the ShowWindow variable of the Win32_ProcessStartup class
VARIANT varParams;
VariantInit(&varParams);
varParams.vt = VT_I2;
varParams.intVal = SW_SHOW;
// And populate it
hres = pStartupInstance->Put(_bstr_t(L"ShowWindow"), 0, &varParams, 0);
// Get the method definition for Win32_Process::Create and store it in pInParamsDefinition
hres = pClass->GetMethod(_bstr_t(lpwMethodName), 0, &pInParamsDefinition, NULL);
// Spawn an instance of the Create method and store it in pParamsInstance
IWbemClassObject* pParamsInstance = NULL;
hres = pInParamsDefinition->SpawnInstance(0, &pParamsInstance);
// Now we can set the parameters without error
hres = pParamsInstance->Put(_bstr_t(L"CurrentDirectory"), 0, pvCurrentDirectory, 0);
请注意,应该检查所有这些hres
返回是否失败。
Win32_ProcessStartup
的定义如下:
https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-processstartup
和Win32_Process
的Create
方法定义:
https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/create-method-in-class-win32-process