Domanda

Durante la modifica di un oggetto COM ATL esistente mi sono imbattuto in un articolo dal blog "The Old New Thing" chiamato "Le vie gente rovinare IUnknown :: QueryInterface" e c'era una discussione nella sezione commenti che è iniziato quando uno degli intervistati (Norman Diamond) ha sottolineato che che in uno dei esempi del articolo che il cast di annullare ** non era corretto.

Tuttavia quando cerco di correggere il mio codice per fare il casting correttamente io alla fine con una perdita di memoria.

L'esempio è stato il seguente:

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

Norman ha detto

  

il punk non è un void *. il punk è un IUnknown *.

     

** vuoto non è un tipo di puntatore universale. void * è un tipo di puntatore universale e char * e parenti sono grandparented in equivalenti in quel modo, ma vuoto ** non lo è.

     

Se si vuole rispettare la convenzione di chiamata e di evitare una morte orribile, devi fare questo:   IUnknown * punk;   void * punkvoid;   psf-> QueryInterface (IID_IUnknown, e punkvoid);   punk = (IUnknown *) punkvoid;

     

Un sacco di altri collaboratori MSDN commesso lo stesso errore identico .... qualcuno potrebbe dire che funziona in tutta la VC ++ implementazioni fino ad oggi, ma che non lo rende codice corretto, ed è ancora violare la convenzione di chiamata.

Alla luce di questo sono andato a cambiare il mio vecchio codice - che è stato il seguente:

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

Poi ho cambiato come segue:

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;

Comunque ora la mia domanda ha una perdita di memoria da questo l'oggetto COM

È stato utile?

Soluzione

È probabile che una perdita di memoria, perché si chiama GetActiveObject() e QueryInterface() che in caso di successo incrementa il conteggio di riferimento per l'oggetto, ma non chiamare Release() più tardi per diminuire il conteggio di riferimento.

Altri suggerimenti

Mmm, penso che anziché assegnare il vuoto * al punk dovrei usare:

pUnk.Attach(reinterpret_cast<IUnknown*>(pUnkVoid));
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top