Verwalten von Objektlebensdauern durch Referenzzählung

Tip

Verwenden Sie intelligente Zeiger anstelle manueller AddRef/ReleaseZeiger. Manuelle Referenzzählung ist die häufigste Ursache für COM-Fehler (nicht freigegebene Objekte, Zugriff nach Freigabe, doppelte Freigabe). Moderner C++-Code sollte intelligente Zeiger verwenden, die die Verweiszählung automatisch behandeln:

#include <wrl/client.h>   // Microsoft::WRL::ComPtr

// ComPtr automatically calls AddRef on copy and Release on destruction
Microsoft::WRL::ComPtr<IStream> stream;
HRESULT hr = CreateStreamOnHGlobal(nullptr, TRUE, &stream);
if (FAILED(hr)) return hr;

// Pass to another interface — use .Get() for raw pointer
// Note: operator& on ComPtr asserts if it already holds a value.
// Use .ReleaseAndGetAddressOf() if the ComPtr may be non-null.
Microsoft::WRL::ComPtr<IUnknown> unknown;
hr = stream.As(&unknown);  // Safe QueryInterface — no manual Release needed

Alternativen:

  • winrt::com_ptr<T> — Intelligenter C++/WinRT-Zeiger (erfordert C++17, integriert in WinRT-Projektionen)
  • wil::com_ptr_nothrow<T>Windows Implementierungsbibliotheken (Ausnahmefreie Alternative)

Die unten beschriebenen manuellen AddRef/Release Regeln sind weiterhin relevant, um zu verstehen, wie COM intern und für die Implementierung von COM-Servern funktioniert. Clientcode sollte diese Methoden jedoch selten direkt aufrufen.

In herkömmlichen Objektsystemen wird der Lebenszyklus von Objekten – d. h. die Probleme bei der Erstellung und Löschung von Objekten – implizit von der Sprache (oder der Sprachlaufzeit) oder explizit von Anwendungsprogrammierern behandelt.

Bei einem sich entwickelnden, dezent aufgebauten System aus wiederverwendeten Komponenten ist es nicht mehr wahr, dass jeder Client oder sogar ein Programmierer immer weiß, wie man mit der Lebensdauer einer Komponente umgehen kann. Für einen Client mit den richtigen Sicherheitsberechtigungen ist es immer noch relativ einfach, Objekte über eine einfache Anforderung zu erstellen, aber das Löschen von Objekten ist eine andere Sache. Es ist nicht unbedingt klar, wenn ein Objekt nicht mehr benötigt wird und gelöscht werden soll. (Leser, die mit garbage-collection-Programmierumgebungen wie Java vertraut sind, sind möglicherweise nicht einverstanden. Java Objekte umfassen jedoch keine Computer- oder sogar Prozessgrenzen, und daher ist die Garbage Collection auf Objekte beschränkt, die in einem einzigen Prozessbereich leben. Darüber hinaus erzwingt Java die Verwendung einer einzelnen Programmiersprache.) Selbst wenn der ursprüngliche Client mit dem Objekt fertig ist, kann es das Objekt nicht einfach herunterfahren, da ein anderer Client oder client möglicherweise noch einen Verweis darauf hat.

Eine Möglichkeit, sicherzustellen, dass ein Objekt nicht mehr benötigt wird, besteht darin, vollständig von einem zugrunde liegenden Kommunikationskanal zu abhängen, um das System zu informieren, wenn alle Verbindungen zu einem prozessübergreifenden oder kanalübergreifenden Objekt verschwunden sind. Schemas, die diese Methode verwenden, sind jedoch aus mehreren Gründen nicht akzeptabel. Ein Problem besteht darin, dass ein großer Unterschied zwischen dem prozessübergreifenden/netzwerkübergreifenden Programmiermodell und dem Einzelprozessprogrammierungsmodell erforderlich sein könnte. Im prozessübergreifenden/netzwerkübergreifenden Programmiermodell würde das Kommunikationssystem die für die Verwaltung der Objektlebensdauer erforderlichen Hooks bereitstellen, während im Einzelprozessprogrammierungsmodell Objekte direkt ohne dazwischenliegenden Kommunikationskanal verbunden sind. Ein weiteres Problem besteht darin, dass dieser Ansatz auch zu einer vom System bereitgestellten Softwareschicht führen könnte, die im In-Process-Fall die Leistung der Komponenten beeinträchtigen würde. Darüber hinaus würde ein Mechanismus, der auf expliziter Überwachung basiert, nicht dazu neigen, auf viele Tausend oder Millionen von Objekten zu skalieren.

COM bietet einen skalierbaren und verteilten Ansatz für diese Reihe von Problemen. Clients teilen einem Objekt mit, wenn sie es verwenden und wenn sie damit fertig sind, und Objekte löschen sich selbst, wenn sie nicht mehr benötigt werden. Dieser Ansatz schreibt vor, dass alle Objekte Verweise auf sich selbst zählen. Programmiersprachen wie Java, die von Haus aus über eigene Verfahren zur Lebensdauerverwaltung verfügen, etwa Garbage Collection, können die Referenzzählung von COM nutzen, um COM-Objekte intern zu implementieren und zu verwenden, sodass sich der Programmierer nicht selbst darum kümmern muss.

Ebenso wie eine Anwendung Arbeitsspeicher freigeben muss, den sie zugewiesen hat, sobald der Arbeitsspeicher nicht mehr verwendet wird, ist ein Client eines Objekts dafür verantwortlich, seine Verweise auf das Objekt freizugeben, wenn dieses Objekt nicht mehr benötigt wird. In einem objektorientierten System kann der Client dies nur tun, indem er dem Objekt eine Anweisung gibt, sich freizugeben.

Es ist wichtig, dass die Zuordnung eines Objekts aufgehoben wird, wenn es nicht mehr verwendet wird. Die Schwierigkeit liegt darin, zu bestimmen, wann es geeignet ist, ein Objekt zu verlagern. Dies ist einfach mit automatischen Variablen (die auf dem Stack alloziert werden) – sie können nicht außerhalb des Blocks verwendet werden, in dem sie deklariert sind, sodass der Compiler sie freigibt, sobald das Ende des Blocks erreicht ist. Bei COM-Objekten, die dynamisch zugeordnet werden, liegt es an den Clients eines Objekts, zu entscheiden, wann sie das Objekt nicht mehr verwenden müssen – insbesondere lokale oder Remoteobjekte, die von mehreren Clients gleichzeitig verwendet werden können. Das Objekt muss warten, bis alle Clients damit fertig sind, bevor sie sich selbst freigeben. Da COM-Objekte über Schnittstellenzeiger bearbeitet werden und von Objekten in verschiedenen Prozessen oder auf anderen Computern verwendet werden können, kann das System die Clients eines Objekts nicht nachverfolgen.

Die Methode, mit der COM bestimmt, wann ein Objekt freigegeben werden soll, ist das manuelle Referenzzählen. Jedes Objekt verwaltet eine Referenzanzahl, mit der nachverfolgt wird, wie viele Clients mit ihm verbunden sind , d. h. wie viele Zeiger auf eine seiner Schnittstellen in einem beliebigen Client vorhanden sind.

Weitere Informationen finden Sie in den folgenden Themen:

Verwenden und Implementieren von IUnknown