Condividi tramite


Gestione della durata di un oggetto

Esiste una regola per le interfacce COM non ancora menzionate. Ogni interfaccia COM deve ereditare, direttamente o indirettamente, da un'interfaccia denominata IUnknown. Questa interfaccia offre alcune funzionalità di base che tutti gli oggetti COM devono supportare.

L'interfaccia IUnknown definisce tre metodi:

Il metodo QueryInterface consente a un programma di eseguire query sulle funzionalità dell'oggetto in fase di esecuzione. In questo argomento verrà illustrato di più, chiedere un oggetto per un'interfaccia. I metodi AddRef e Release vengono usati per controllare la durata di un oggetto. Questo è l'argomento di questo argomento.

Conteggio dei riferimenti

Qualsiasi altra operazione può fare un programma, a un certo punto alloca e libera risorse. L'allocazione di una risorsa è semplice. Sapere quando liberare la risorsa è difficile, soprattutto se la durata della risorsa si estende oltre l'ambito corrente. Questo problema non è univoco per COM. Qualsiasi programma che alloca memoria heap deve risolvere lo stesso problema. Ad esempio, C++ usa distruttori automatici, mentre C# e Java usano Garbage Collection. COM usa un approccio denominato conteggio dei riferimenti.

Ogni oggetto COM gestisce un conteggio interno. Questo valore è noto come conteggio dei riferimenti. Il conteggio dei riferimenti tiene traccia del numero di riferimenti all'oggetto attualmente attivo. Quando il numero di riferimenti scende a zero, l'oggetto si elimina. L'ultima parte vale la pena ripetere: l'oggetto si elimina. Il programma non elimina mai in modo esplicito l'oggetto.

Ecco le regole per il conteggio dei riferimenti:

  • Quando l'oggetto viene creato per la prima volta, il numero di riferimenti è 1. A questo punto, il programma ha un singolo puntatore all'oggetto.
  • Il programma può creare un nuovo riferimento duplicando (copiando) il puntatore. Quando si copia il puntatore, è necessario chiamare il metodo AddRef dell'oggetto. Questo metodo incrementa il conteggio dei riferimenti per uno.
  • Al termine dell'uso di un puntatore all'oggetto, è necessario chiamare Release. Il metodo Release decrementa il conteggio dei riferimenti per uno. Invalida anche il puntatore. Non usare di nuovo il puntatore dopo aver chiamato Release. Se sono presenti altri puntatori allo stesso oggetto, è possibile continuare a usare tali puntatori.
  • Quando è stato chiamato Release con ogni puntatore, il conteggio dei riferimenti dell'oggetto raggiunge zero e l'oggetto si elimina.

Il diagramma seguente mostra un caso semplice ma tipico.

Diagramma che mostra un semplice caso di conteggio dei riferimenti.

Il programma crea un oggetto e archivia un puntatore (p) all'oggetto. A questo punto, il conteggio dei riferimenti è 1. Al termine del programma usando il puntatore, chiama Release. Il conteggio dei riferimenti viene decrementato su zero e l'oggetto si elimina. Ora p non è valido. Si tratta di un errore da usare per eventuali chiamate di metodo aggiuntive.

Il diagramma successivo mostra un esempio più complesso.

figura che mostra il conteggio dei riferimenti

In questo caso, il programma crea un oggetto e archivia il puntatore p, come prima. Successivamente, il programma copia p in una nuova variabile, q. A questo punto, il programma deve chiamare AddRef per incrementare il conteggio dei riferimenti. Il conteggio dei riferimenti è ora 2 e all'oggetto sono presenti due puntatori validi. Si supponga ora che il programma venga completato usando p. Il programma chiama Release, il conteggio dei riferimenti passa a 1 e p non è più valido. Tuttavia, q è ancora valido. Successivamente, il programma termina l'uso di q. Pertanto, chiama di nuovo Release . Il conteggio dei riferimenti passa a zero e l'oggetto si elimina.

Potresti chiederti perché il programma copiava p. Esistono due motivi principali: in primo luogo, è possibile archiviare il puntatore in una struttura di dati, ad esempio un elenco. In secondo luogo, potrebbe essere necessario mantenere il puntatore oltre l'ambito corrente della variabile originale. Pertanto, si copia in una nuova variabile con ambito più ampio.

Un vantaggio del conteggio dei riferimenti è che è possibile condividere puntatori tra sezioni diverse del codice, senza i vari percorsi di codice che coordinano per eliminare l'oggetto. Ogni percorso di codice chiama invece Release quando il percorso del codice viene eseguito usando l'oggetto . L'oggetto gestisce l'eliminazione in modo corretto al momento corretto.

Esempio

Di seguito è riportato di nuovo il codice della finestra di dialogo Apri .

HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED |
    COINIT_DISABLE_OLE1DDE);

if (SUCCEEDED(hr))
{
    IFileOpenDialog *pFileOpen;

    hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
            IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

    if (SUCCEEDED(hr))
    {
        hr = pFileOpen->Show(NULL);
        if (SUCCEEDED(hr))
        {
            IShellItem *pItem;
            hr = pFileOpen->GetResult(&pItem);
            if (SUCCEEDED(hr))
            {
                PWSTR pszFilePath;
                hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
                if (SUCCEEDED(hr))
                {
                    MessageBox(NULL, pszFilePath, L&quot;File Path&quot;, MB_OK);
                    CoTaskMemFree(pszFilePath);
                }
                pItem->Release();
            }
        }
        pFileOpen->Release();
    }
    CoUninitialize();
}

Il conteggio dei riferimenti si verifica in due posizioni nel codice. Prima di tutto, se il programma crea correttamente l'oggetto Common Item Dialog, deve chiamare Release sul puntatore pFileOpen .

hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, 
        IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

if (SUCCEEDED(hr))
{
    // ...
    pFileOpen->Release();
}

In secondo luogo, quando il metodo GetResult restituisce un puntatore all'interfaccia IShellItem , il programma deve chiamare Release sul puntatore pItem .

hr = pFileOpen->GetResult(&pItem);

if (SUCCEEDED(hr))
{
    // ...
    pItem->Release();
}

Si noti che in entrambi i casi, la chiamata release è l'ultima cosa che accade prima che il puntatore esce dall'ambito. Si noti anche che Release viene chiamato solo dopo aver testato HRESULT per l'esito positivo. Ad esempio, se la chiamata a CoCreateInstance ha esito negativo, il puntatore pFileOpen non è valido. Pertanto, si tratta di un errore per chiamare Release sul puntatore.

Prossima

Richiesta di un oggetto per un'interfaccia