Как правильно использовать приведение при использовании ATL и IUnknownPtr? - PullRequest
0 голосов
/ 08 марта 2010

Во время модификации существующего COM-объекта ATL я наткнулся на статью из блога «The Old New Thing» под названием «Как люди путают IUnknown :: QueryInterface», и в разделе комментариев началось обсуждение, которое началось, когда один из респондентов (Норман Даймонд) указал на то, что в одном из примеров статьи неправильное приведение в качестве **.

Однако, когда я пытаюсь исправить свой код для правильного преобразования, я получаю утечку памяти.

Пример был следующим:

IShellFolder *psf = some object;
IUnknown *punk = NULL;
psf->QueryInterface(IID_IUnknown, (void**)&punk);

Норман сказал

панк не пустота *. панк это IUnknown *.

void ** не является универсальным типом указателя. void * - это универсальный тип указателя, и char * и его родственники дедулируются таким образом, чтобы быть эквивалентными, а void ** - нет.

Если вы хотите соблюдать правила вызова и избегать ужасных смертей, вы должны сделать это: IUnknown * punk; void * punkvoid; psf-> QueryInterface (IID_IUnknown, & punkvoid); punk = (IUnknown *) punkvoid;

Многие другие участники MSDN совершили ту же самую ошибку ... некоторые люди могут сказать, что на сегодняшний день это работает во всех реализациях VC ++, но это не делает его правильным кодом, и оно все еще нарушает соглашение о вызовах.

В свете этого я пошел, чтобы изменить мой старый код - который был следующим:

#include <comdef.h>

...

HRESULT FinalConstruct()
{ 
    if (m_dwROTCookie != 0)
        return E_FAIL;

    //Check whether there already is an instance of the Object
    IUnknownPtr pUnk = NULL;
    if (GetActiveObject(CLSID_Object, NULL, &pUnk) == S_OK)
    {
        TRACE_WARNING("An instance of Object already exists in the current context");
        return S_OK;
    }
    HRESULT hr = QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&pUnk));

    hr = RegisterActiveObject(pUnk, CLSID_Object, ACTIVEOBJECT_WEAK, m_dwROTCookie);        
    if (FAILED(hr))
        return hr;

    hr = CoLockObjectExternal(pUnk, TRUE, TRUE);
    pUnk = NULL;
    ATLASSERT(m_dwRef == 2);
    return hr;
}

Затем я изменил его следующим образом:

HRESULT FinalConstruct()
{ 
    if (m_dwROTCookie != 0)
        return E_FAIL;

    //Check whether there already is an instance of the Object
    IUnknownPtr pUnk = NULL;
    if (GetActiveObject(CLSID_Object, NULL, &pUnk) == S_OK)
    {
        TRACE_WARNING("An instance of Object already exists in the current context");
        return S_OK;
    }
    void* pUnkVoid = NULL;
    HRESULT hr = QueryInterface(IID_IUnknown, &pUnkVoid);

    if (SUCCEEDED(hr)
    {
        pUnk = reinterpret_cast<IUnknown*>(pUnkVoid);
        hr = RegisterActiveObject(pUnk, CLSID_Object, ACTIVEOBJECT_WEAK, m_dwROTCookie);        
        if (FAILED(hr))
            return hr;

        hr = CoLockObjectExternal(pUnk, TRUE, TRUE);
        pUnk = NULL;
    }
    ATLASSERT(m_dwRef == 2);

    return hr;

Однако теперь у моего приложения есть утечка памяти из-за этого COM-объекта

Ответы [ 2 ]

0 голосов
/ 09 марта 2010

Возможно, у вас утечка памяти, потому что вы вызываете GetActiveObject() и QueryInterface(), которые в случае успеха увеличивают счетчик ссылок на объект, но не вызывают Release() позже, чтобы уменьшить счетчик ссылок.

0 голосов
/ 08 марта 2010

Ммм, я думаю, что вместо того, чтобы назначать пустоту * pUnk, я должен использовать:

pUnk.Attach(reinterpret_cast<IUnknown*>(pUnkVoid));
...