内存泄漏IWbemServices-> ExecNotificationQuery?

时间:2016-10-28 20:43:09

标签: c++ winapi memory-leaks wmi wmi-query

我有一个与WMI相关的代码,一旦启动新应用程序就会获得一个事件。 我已经跳过了初始化部分,这里是代码。请注意,一切正常,所有HRESULT都是S_OK。

IEnumWbemClassObject* pEnumerator = NULL;

pSvc->ExecNotificationQuery( // IWbemServices *pSvc is initialized
    bstr_t("WQL"),
    bstr_t("SELECT * FROM __InstanceCreationEvent WITHIN 1 "
        "WHERE TargetInstance ISA 'Win32_Process'"),
    WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
    NULL, &pEnumerator);

while (pEnumerator) {
    _variant_t v1, v2;
    pclsObj->Get(_bstr_t(L"TargetInstance"), 0, &v1, 0, 0);
    IUnknown* str = v1;
    str->QueryInterface(IID_IWbemClassObject, reinterpret_cast< void** >(&pclsObj));
    pclsObj->Get(bstr_t(L"Handle"), 0, &v2, 0, 0);
    LONG pid{ 0 };
    hr = VarI4FromStr(v2.bstrVal, LOCALE_NOUSEROVERRIDE, 409, &pid);
    Internal::Inject(pid); // It's my code, not relevant here

    str->Release();
    pclsObj->Release();
    v1.Clear();
    v2.Clear();
}

此代码取自MSDN并稍作修改。然而,它泄漏了记忆,我不明白为什么。通过MSVC内存分析器查看给我们这张图片: screen1

或者: screen2

从我的角度来看 - 我已经清除\释放了所有内容,然而,一旦新事件到来并且它们永远存在,分配就像屏幕截图一样。

我发现this question,似乎是相同的,但没有收到回复。

Visual Studio 2015 Update 3,最新的Windows 10 x64 Professional。

1 个答案:

答案 0 :(得分:1)

当您致电str->QueryInterface()时,您将覆盖pclsObj指针而不事先致电pclsObj->Release()。您在Release()返回的pclsObj对象上调用了QueryInterface(),并泄露了原始pclsObj对象。

您应该手动停止管理接口引用计数,而是使用_com_ptr_t包装器。

原始pclsObj来自何处?您似乎错过了对pEnumerator->Next()的调用。

尝试更类似的东西(为简洁省略错误处理):

_com_ptr_t<IEnumWbemClassObject> pEnumerator;

pSvc->ExecNotificationQuery( // IWbemServices *pSvc is initialized
    bstr_t("WQL"),
    bstr_t("SELECT * FROM __InstanceCreationEvent WITHIN 1 "
        "WHERE TargetInstance ISA 'Win32_Process'"),
    WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
    NULL, &pEnumerator);

if (pEnumerator)
{
    while (true)
    {
        _com_ptr_t<IWbemClassObject> pclsEvent, pclsObj;
        _variant_t v1, v2;
        ULONG ulReturned = 0;

        pEnumerator->Next(WBEM_INFINITE, 1, &pclsEvent, &ulReturned);
        pclsEvent->Get(_bstr_t(L"TargetInstance"), 0, &v1, 0, 0);

        _com_ptr_t<IUnknown> str = v1;
        str->QueryInterface(IID_IWbemClassObject, reinterpret_cast<void**>(&pclsObj));
        pclsObj->Get(bstr_t(L"Handle"), 0, &v2, 0, 0);

        LONG pid{ 0 };
        hr = VarI4FromStr(v2.bstrVal, LOCALE_NOUSEROVERRIDE, 409, &pid);
        Internal::Inject(pid); // It's my code, not relevant here
    }
}

可替换地:

_com_ptr_t<IEnumWbemClassObject> pEnumerator;

pSvc->ExecNotificationQuery( // IWbemServices *pSvc is initialized
    bstr_t("WQL"),
    bstr_t("SELECT * FROM __InstanceCreationEvent WITHIN 1 "
        "WHERE TargetInstance ISA 'Win32_Process'"),
    WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
    NULL, &pEnumerator);

if (pEnumerator)
{
    while (true)
    {
        _com_ptr_t<IWbemClassObject> pclsObj;
        _variant_t v1, v2;
        ULONG ulReturned = 0;

        pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &ulReturned);
        pclsObj->Get(_bstr_t(L"TargetInstance"), 0, &v1, 0, 0);

        // _com_ptr_t::operator&() calls Release() on the current object
        // if not NULL before then returning the address of the the
        // interface pointer...

        _com_ptr_t<IUnknown> str = v1;
        str->QueryInterface(IID_IWbemClassObject, reinterpret_cast<void**>(&pclsObj));
        pclsObj->Get(bstr_t(L"Handle"), 0, &v2, 0, 0);

        LONG pid{ 0 };
        hr = VarI4FromStr(v2.bstrVal, LOCALE_NOUSEROVERRIDE, 409, &pid);
        Internal::Inject(pid); // It's my code, not relevant here
    }
}