I'm trying to learn how the WMI works, but the default examples given have so far been atrocious.
Here's the example for calling the Create
method of the Win32_Process
class:
https://learn.microsoft.com/en-us/windows/win32/wmisdk/example--calling-a-provider-method
I have added proper error handling to this, we store the HRESULT of each call in a variable hres
and check if the calls failed. As such:
hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
if (FAILED(hres))
{
wprintf("Failed to get class. Error code = 0x%lx\n", hres);
return hres;
}
The code executes correctly right up until here:
// 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: %s\n", V_BSTR(&varCommand));
Where the pClassInstance->Put
throws 'ol c5.
At this point, hres
is S_OK
for the call to SpawnInstance
but these are the pointers we have for the class instances:
+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 *
And varCommand
:
+varCommand BSTR = 0x000001c04e74ffe8 tagVARIANT
The call stack:
oleaut32.dll!SysAllocString()
vfbasics.dll!AVrfpSysAllocString()
wbemcomn.dll!CVar::SetVariant()
fastprox.dll!CWbemInstance::Put()
> Ele.exe!WMIConnection::InvokeMethod()
So it appears that bstrVal isn't being properly set, I think? I tried initializing it first with VariantInit
, and I also tried dynamically allocating it on the heap instead. Neither resolved the issue:
VARIANT varCommand;
VariantInit(&varCommand);
varCommand.vt = VT_BSTR;
varCommand.bstrVal = _bstr_t(L"notepad.exe");
I also tried manually zeroing out the Variant buffer, to no effect. This is what we have for bstrVal
in the memory dump when the access violation occurs:
bstrVal 0x000001c04e74ffe8 <Error reading characters of string.> wchar_t *
<Unable to read memory> wchar_t
On this line:
varCommand.bstrVal = _bstr_t(L"notepad.exe");
The code creates a temporary _bstr_t
object that goes out of scope, destroying the allocated BSTR
memory, immediately after varCommand.bstrVal
has been assigned to. Thus, varCommand.bstrVal
is left dangling, pointing at invalid memory, when varCommand
is passed to pClassInstance->Put()
. That is undefined behavior.
Use this instead to keep the BSTR
alive until you are actually done using it:
_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);
Alternatively:
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);
Otherwise, consider using _variant_t
instead, let it manage the memory for you:
_variant_t varCommand(L"notepad.exe");
hres = pClassInstance->Put(L"CommandLine", 0, &varCommand, 0);
wprintf(L"The command is: %s\n", V_BSTR(&varCommand));