c++ 官方Microsoft WMI示例中的访问冲突

h9a6wy2h  于 2023-01-14  发布在  其他
关注(0)|答案(2)|浏览(208)

我试图学习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%lx\n", 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: %s\n", V_BSTR(&varCommand));

pClassInstance->Put抛出'ol c5的地方。
此时,对于SpawnInstance的调用,hresS_OK,但这些是我们为类示例提供的指针:

+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 *

varCommand

+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
aiazj4mn

aiazj4mn1#

我想通了。网上有几个论坛帖子询问这个例子,没有给出解决方案,所以我现在很高兴提供这个。
Microsoft示例使用了不正确的类。
在Microsoft示例中,它们尝试调用Win32_Process类示例上的Put方法来设置参数。
这是不正确的。我们需要先获取Win32_Process::Create的类方法定义,然后在Win32_Process::Create的一个 * 新示例 * 中设置它的参数。
我们还需要构造Win32_ProcessStartup类对象的示例,因为它是Win32_Process::Create的必需输入参数。
在下面的示例中,我将填充Win32_ProcessStartup类示例的一个字段,您可以计算其余的字段。
这段代码来自Microsoft示例:

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: %s\n", 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_ProcessCreate方法的定义:
https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/create-method-in-class-win32-process

kokeuurv

kokeuurv2#

varCommand.bstrVal = _bstr_t(L"notepad.exe");

这行代码创建了一个超出作用域的 * 临时 * _bstr_t对象,在将varCommand.bstrVal赋值给varCommand.bstrVal之后,该对象将销毁已分配的BSTR内存。因此,当varCommand传递给pClassInstance->Put()时,varCommand.bstrVal处于悬空状态,指向无效内存。这是 * 未定义的行为 *。
请改用此命令来保持BSTR处于活动状态,直到您实际使用完它:

_bstr_t str(L"notepad.exe");
varCommand.bstrVal = str;

否则,请改用_variant_t

// Create the values for the in parameters
_variant_t varCommand(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));

相关问题