Verwalten der Lebensdauer eines Objekts

Es gibt eine Regel für COM-Schnittstellen, die wir noch nicht erwähnt haben. Jede COM-Schnittstelle muss direkt oder indirekt von einer Schnittstelle namens IUnknown erben. Diese Schnittstelle bietet einige Baselinefunktionen, die alle COM-Objekte unterstützen müssen.

Die IUnknown-Schnittstelle definiert drei Methoden:

Mit der QueryInterface-Methode kann ein Programm die Funktionen des Objekts zur Laufzeit abfragen. Weitere Informationen dazu finden Sie im nächsten Thema, Asking an Object for an Interface. Die Methoden AddRef und Release werden verwendet, um die Lebensdauer eines Objekts zu steuern. Dies ist das Thema dieses Themas.

Verweiszählung

Was auch immer ein Programm sonst tun kann, zu einem bestimmten Zeitpunkt werden Ressourcen reserviert und frei. Das Zuweisen einer Ressource ist einfach. Es ist schwierig, zu wissen, wann die Ressource frei werden soll, insbesondere, wenn die Lebensdauer der Ressource den aktuellen Bereich überdehnt. Dieses Problem ist nicht für COM eindeutig. Jedes Programm, das Heapspeicher zuteilen, muss das gleiche Problem lösen. Beispielsweise verwendet C++ automatische Destruktoren, während C# und Java die Garbage Collection verwenden. COM verwendet einen Ansatz namens Verweiszählung.

Jedes COM-Objekt verwaltet eine interne Anzahl. Dies wird als Verweisanzahl bezeichnet. Die Verweisanzahl verfolgt, wie viele Verweise auf das Objekt derzeit aktiv sind. Wenn die Anzahl der Verweise auf 0 (null) fällt, löscht sich das Objekt selbst. Der letzte Teil lohnt sich zu wiederholen: Das -Objekt löscht sich selbst. Das Programm löscht das -Objekt nie explizit.

Hier sind die Regeln für die Verweiszählung:

  • Wenn das Objekt zum ersten Mal erstellt wird, beträgt die Verweisanzahl 1. An diesem Punkt verfügt das Programm über einen einzelnen Zeiger auf das -Objekt.
  • Das Programm kann einen neuen Verweis erstellen, indem der Zeiger dupliziert (kopiert) wird. Wenn Sie den Zeiger kopieren, müssen Sie die AddRef-Methode des -Objekts aufrufen. Diese Methode erhöht den Verweiszähler um eins.
  • Wenn Sie mit der Verwendung eines Zeigers auf das -Objekt fertig sind, müssen Sie Release aufrufen. Die Release-Methode dekrementiert den Verweiszähler um eins. Außerdem wird der Zeiger ungültig. Verwenden Sie den Zeiger nach dem Aufruf von Release nicht mehr. (Wenn Sie andere Zeiger auf dasselbe Objekt haben, können Sie diese Zeiger weiterhin verwenden.)
  • Wenn Sie Release mit jedem Zeiger aufgerufen haben, erreicht die Objektverweisanzahl des Objekts null, und das Objekt löscht sich selbst.

Das folgende Diagramm zeigt einen einfachen, aber typischen Fall.

Diagramm, das einen einfachen Fall der Verweiszählung zeigt.

Das Programm erstellt ein -Objekt und speichert einen Zeiger (p) auf das -Objekt. An diesem Punkt ist der Verweiszähler 1. Wenn das Programm mit dem Zeiger fertig ist, ruft es Release auf. Die Verweisanzahl wird auf 0 (null) reduziert, und das Objekt löscht sich selbst. Jetzt ist p ungültig. Es ist ein Fehler, p für alle weiteren Methodenaufrufe zu verwenden.

Das nächste Diagramm zeigt ein komplexeres Beispiel.

Abbildung, die die Verweiszählung zeigt

Hier erstellt das Programm ein -Objekt und speichert den Zeiger p wie zuvor. Als Nächstes kopiert das Programm p in eine neue Variable, q. An diesem Punkt muss das Programm AddRef aufrufen, um die Verweisanzahl zu erhöhen. Die Verweisanzahl ist jetzt 2, und es gibt zwei gültige Zeiger auf das -Objekt. Nehmen wir nun an, dass das Programm mit p beendet ist. Das Programm ruft Release auf,die Verweisanzahl wird auf 1 angerechnet, und p ist nicht mehr gültig. q ist jedoch weiterhin gültig. Später wird das Programm mit q beendet. Daher wird Release erneut aufruft. Die Verweisanzahl wird auf 0 (null) gesetzt, und das Objekt löscht sich selbst.

Sie fragen sich vielleicht, warum das Programm p kopieren würde. Es gibt zwei Hauptgründe: Erstens möchten Sie den Zeiger möglicherweise in einer Datenstruktur speichern, z. B. in einer Liste. Zweitens sollten Sie den Zeiger über den aktuellen Bereich der ursprünglichen Variablen hinaus behalten. Daher würden Sie sie in eine neue Variable mit einem größeren Bereich kopieren.

Ein Vorteil der Verweiszählung ist, dass Sie Zeiger über verschiedene Codeabschnitte hinweg freigeben können, ohne dass die verschiedenen Codepfade koordiniert werden, um das Objekt zu löschen. Stattdessen ruft jeder Codepfad nur Release auf, wenn dieser Codepfad mithilfe des -Objekts erfolgt. Das -Objekt verarbeitet das Löschen selbst zum richtigen Zeitpunkt.

Beispiel

Hier sehen Sie den Code aus dem Beispiel des Dialogfelds Öffnen erneut.

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();
}

Die Verweiszählung erfolgt an zwei Stellen in diesem Code. Wenn das Programm das Common Item Dialog-Objekt erfolgreich erstellt, muss es zunächst Release für den pFileOpen-Zeiger aufrufen.

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

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

Zweitens: Wenn die GetResult-Methode einen Zeiger auf die IShellItem-Schnittstelle zurückgibt, muss das Programm Release für den pItem-Zeiger aufrufen.

hr = pFileOpen->GetResult(&pItem);

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

Beachten Sie, dass in beiden Fällen der Release-Aufruf das letzte Mal ist, bevor der Zeiger den Gültigkeitsbereich übergeht. Beachten Sie auch, dass Release erst aufgerufen wird, nachdem Sie HRESULT auf Erfolg testen. Wenn beispielsweise der Aufruf von CoCreateInstance fehlschlägt, ist der pFileOpen-Zeiger ungültig. Daher wäre es ein Fehler, Release für den Zeiger auf aufruft.

Nächste

Fragen eines Objekts nach einer Schnittstelle