Hauptseite >Tips zu VB5/6 >  COM Referenzzählung     view this document in English view this document in English
 
Im Netz findet man einige Links (1, 2) mit Funktionen, mit denen man herausbekommen kann, von wievielen Objektreferenzen eine COM-Objektinstanz momentan "am Leben erhalten" wird. Das ist oft recht nützlich, wenn man z.B. eine Objektreferenz an andere Objekte übergeben will, von denen dann der letzte "Konsument" diverse Aufräumarbeiten übernehmen soll (z.B. Aufruf einer .Close-Methode o.ä.).

Die genannten Funktionen lesen den Referenzzähler an einem festen Offset hinter dem Objektzeiger (ObjPtr) aus. Das funktioniert auch einwandfrei, ist so aber offensichtlich nur auf Objekte anwendbar, die mit VB5/6 kompiliert wurden. Denn wenn man die o.a. Funktionen beispielweise auf eine DAO.Database oder eine VB.Collection anwendet, werden ziemlich unplausible Werte zurückgegeben. Das ist nicht ganz überraschend, da die COM-Spezifikation für IUnknown nur die Funktionen QueryInterface, AddRef und Release vorschreibt, aber den Referenzzähler selbst nicht offenlegt. Bei VB5/6 weiss man offenbar mittlerweile (wohl per Trial & Error), wo der abgelegt ist.

Für in anderen Sprachen geschriebene COM-Komponenten benötigen wir also einen anderen Ansatz. Und der macht sich zunutze, dass die COM-Spezifikation den Referenzzähler eben doch, wenn auch nur indirekt offenlegt (danke an Olaf Schmidt für den Hinweis); im MSDN steht zu IUnknown.AddRef bzw. -Release:

Return Value
    This method returns the new reference count.
    This value is intended to be used only for test purposes.


Somit kommt man an den Referenzzähler, indem man "paarweise" AddRef und Release aufruft und den Rückgabewert auswertet.

Damit VB die Schnittstelle IUnknown kennt, müssen wir zuvor Eduardo A. Morcillos OLE interfaces & functions TLB in das Projekt einbinden (unter Projekt → Verweise).

Dann kapseln wir die oben beschriebene Vorgehensweise in folgender Funktion:

Public Function GetRefCount(ByVal oToCheck As Object) As Long
Dim UnkToCheck As olelib.IUnknown

  If Not oToCheck Is Nothing Then
    'casten auf IUnknown
    Set UnkToCheck = oToCheck
    'Referenzzähler erhöhen
    UnkToCheck.AddRef
    'Referenzzähler wieder verringern und um lokale Referenzen korrigieren
    GetRefCount = UnkToCheck.Release - 3
  End If
End Function
Verwendung und Test der Funktion:

Sub Main()
Dim ColInstance As Collection
Dim ColRef2 As Collection, ColRef3 As Collection

  Set ColInstance = New Collection 'instanziieren (Counter auf 1)
  Set ColRef2 = ColInstance 'weitere Referenz setzen (Counter jetzt auf 2)
  Set ColRef3 = ColInstance 'weitere Referenz setzen (Counter jetzt auf 3)

  Debug.Print GetRefCount(ColRef3) 'ergibt 3
End Sub
Die Funktion liefert zuverlässig den Stand des Referenzzählers, unabhängig davon, in welcher Sprache die Komponente geschrieben wurde. Allerdings müssen Sie sich darauf verlassen, dass AddRef und Release auch tatsächlich spezifikations-konform implementiert wurden; während VB dies automatisch für uns erledigt, obliegt es in anderen Sprachen möglicherweise der Sorgfalt des Entwicklers, den erwarteten Rückgabewert auch zu setzen. Sie sollten also jede Komponente, die Sie mit obiger Methode untersuchen wollen, zuvor auf spezifikations-konformes Verhalten testen. Ausserdem sollten Sie diese Methode nur mit InProcess-COM-Servern verwenden; bei OutOfProcess-Servern kommt das sog. "Marshalling" ins Spiel, womit der Durchgriff auf den Referenzzähler so oder so nicht mehr möglich ist.

Wenn Sie diese Einschränkungen beachten, haben Sie somit ein recht hilfreiches Werkzeug zur Hand, auch zum Debuggen. Man kann z.B. mit

Debug.Assert GetRefCount(x) = 1
Set x = Nothing
sicherstellen, dass die Instanz x an dieser Stelle auch wirklich terminiert.

Falls GetRefCount(x) nicht das erwartete Ergebnis liefert, kann man den Ausdruck in der IDE als Überwachungsausdruck einsetzen und so rauskriegen, wo unerwünschte Zusatz-Referenzen erzeugt werden.

Nebenbei ermöglicht die Methode interessante Beobachtungen: bei einer DAO.Database z.B. sollte man eigentlich erwarten, dass der Referenzzähler immer mindestens 2 beträgt, da neben der ursprünglichen Variablen ja noch die Databases-Collection des übergeordneten Workspace-Objekts auf die Instanz zeigt. Dem ist aber nicht so. Offenbar ist die Databases-Collection also eine "Weak-References-Collection".
Hauptseite >  Tips zu VB5/6 >  diese Seite