2016-10-28 4 views
-1

У меня есть код, связанный с WMI, который получает событие после запуска нового приложения. Я пропустил часть инициализации, вот код. Обратите внимание, что все работает, все HRESULT - S_OK.Утечка памяти в IWbemServices-> ExecNotificationQuery?

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.

+2

ли называть вас в Release() на pEnumerator? Это также COM-объект – Matt

+0

@matt, я пробовал, не помог – Starl1ght

+0

попробуйте этот инструмент DebugDiag: https://www.microsoft.com/en-us/download/details.aspx?id=49924 – Matt

ответ

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 
    } 
} 
+1

Существует также удобный [IID_PPV_ARGS] (https://msdn.microsoft.com/en-us/library/windows/desktop/ee330727.aspx) макрос, который позволяет вам писать: 'str-> QueryInterface (IID_PPV_ARGS (& pclsObj));' , Это не только короче, но и безопаснее, поскольку интерфейс ID и тип указателя гарантированно совпадают. – IInspectable