| An den Instanzzeiger einer Klasseninstanz, der als erster Parameter der WindowProc erwartet wird, gelangt man über die undokumentierte Funktion Instanzzeiger = ObjPtr(Klasseninstanz) Listing 5 respektive innerhalb der fraglichen Klasse selbst über Instanzzeiger = ObjPtr(Me) Listing 6 Nun muss noch die Adresse der sich nun in einer Klasse befindlichen WindowProc ermittelt werden, damit diese aus dem Wrapper heraus aufgerufen werden kann. Da es sich wie weiter oben erwähnt bei einer Klasse in VB letztendlich um ein COM-Objekt handelt, auf dessen Methoden möglicherweise auch andere Sprachen zugreifen können sollen, müssen die Adressen zur Laufzeit ermittelbar sein. Der von COM dazu genutzte Mechanismus nennt sich vTable-Binding. Dabei ist obiger Instanzzeiger nichts anderes als ein Zeiger auf einen Speicherbereich. Folgt man der vom Zeiger beschriebenen Adresse, so erhält man den vPtr, einen Zeiger, der wiederum auf das erste Element einer Tabelle zeigt, in der alle öffentlichen Methoden des Objekts aufgeführt sind die vTable (virtual method table) für das standardmäßige Interface, in diesem Fall das Interface der implementierten Klasse. Der Vollständigkeit halber sei an dieser Stelle noch erwähnt, dass ein Objekt durchaus mehrere vTables besitzen kann, abhängig davon, wie viele Interfaces implementiert werden. Aus diesem Grund muss jede vTable als erste Einträge die drei Methoden des wiederum von COM beschriebenen IUnknown-Interfaces auflisten: Public Function QueryInterface(ByVal riid As IID) As Long
Public Function AddRef() As Long
Public Function Release() As Long Listing 7 AddRef() erhöht den internen Referenzzähler, der verhindert, dass eine noch benötigte Instanz der Klasse gelöscht wird; Release() erniedrigt diesen wieder und löscht bei Bedarf die Instanz. Von erweitertem Interesse ist die Funktion QueryInterface(). Dieser wird die GUID (global unique identifier, ein 128 Bit breiter Wert, der mit einer Wahrscheinlichkeit von 1:2^128 weltweit eindeutig ist) des gesuchten Interfaces übergeben. Wird dieses Interface durch das Objekt implementiert, so liefert die Funktion einen Zeiger auf das Interface zurück, von dem ausgehend wieder die zugehörige vTable erreicht werden kann. Damit bleibt nur noch die Frage, an welcher Stelle der Tabelle die gesuchte Funktion zu finden ist. Leider lässt sich dieses Problem nicht auf triviale Weise lösen, da Bezeichner, also auch Funktionsnamen, durch den Kompilationsvorgang verloren gehen. COM löst dieses Problem dadurch, dass jedes implementierte Interface eine eigene vTable besitzt, deren Aufbau sich daraus ergibt, dass seitens der zugehörigen Interfaces eine bestimmte Reihenfolge der Methoden definiert ist. Diesen Umstand kann man sich nun zunutze machen, da der VB-Compiler öffentliche Funktionen gemäß ihrer Reihenfolge in der vTable einträgt. Damit gilt im Weiteren der allgemeine Grundsatz, dass die Callback-Funktion öffentlich ist und in der Quellcodedatei unmittelbar auf den Deklarationsteil folgt. Dies führt für Klassen, sowie deren spezialisierten Geschwistern, Usercontrol und Form, zu festen Positionen in der Tabelle, an denen die gesuchten Adressen zu finden sind. Im Code folgt man dazu dem Instanzzeiger CopyMemory vPtr, ByVal Instanzzeiger, 4 Listing 8 und liest im Falle einer Klasse das achte Element CopyMemory CallbackAddress, ByVal (vPtr + 7 * 4), 4 Listing 9 im Falle einer Form das vierhundertsiebenundvierzigste Element CopyMemory CallbackAddress, ByVal (vPtr + 446 * 4), 4 Listing 10 im Falle eines Usercontrols das vierhundertneunzigste Element CopyMemory CallbackAddress, ByVal (vPtr + 489 * 4), 4 Listing 11 An dieser Stelle sei nochmals eindringlich daran erinnert, auf obige Konvention zu achten. Liest man vom falschen vTable-Eintrag, so wird man mindestens Fehlfunktionen des Programms beobachten, in den meisten Fällen unmittelbar in Form eines Absturzes. Auch sollte man von öffentlich deklarierten Variablen Abstand nehmen, da diese auch Einfluss auf den Aufbau der vTable nehmen. Lässt sich dies gar nicht vermeiden, so sind für jede Variable acht Bytes zum Offset zu addieren. |