Pergunta

Durante a modificação de um objeto COM ATL existente me deparei com um artigo do "The Old New Thing" blog chamado "Os caminhos pessoas asneira IUnknown :: QueryInterface" e houve uma discussão na seção de comentários que começou quando um dos entrevistados (Norman diamante) apontou que que em um dos exemplos do artigo que o elenco de anular ** estava incorreta.

No entanto, quando eu tentar corrigir o meu código para fazer o casting corretamente eu acabar com um vazamento de memória.

O exemplo foi a seguinte:

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

Norman disse

Punk não é um * vazio. punk é um IUnknown *.

vazio ** não é um tipo de ponteiro universal. void * é um tipo de ponteiro universal, e char * e parentes estão grandparented em ser equivalente dessa forma, mas vazio ** não é.

Se você quiser obedecer a convenção de chamada e evitar mortes horríveis, que você tem para fazer isso: IUnknown * do punk; void * punkvoid; PSF> QueryInterface (IID_IUnknown, & punkvoid); Punk = (IUnknown *) punkvoid;

Muitos outros contribuidores MSDN cometeu o mesmo erro idêntico .... algumas pessoas podem dizer que ele funciona em todas as implementações VC ++ até à data, mas que não torná-lo código correto, e ainda está violando a convenção de chamada.

À luz disto eu fui mudar meu velho código - que foi o seguinte:

#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;
}

Eu, então, mudou-lo da seguinte forma:

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;

No entanto, agora o meu aplicativo tem um vazamento de memória a partir deste Objeto COM

Foi útil?

Solução

Você provavelmente tem um vazamento de memória porque você chamar GetActiveObject() e QueryInterface() que após o sucesso incrementar a contagem de referência no objeto, mas não chame Release() mais tarde para diminuir a contagem de referência.

Outras dicas

Mmm, acho que em vez de atribuir o * vazio ao punk eu deveria estar usando:

pUnk.Attach(reinterpret_cast<IUnknown*>(pUnkVoid));
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top