|
Sind Sie auch schon an dieser Fehlermeldung verzweifelt, wenn Sie versucht haben, eine Referenz auf das UserDocument-Objekt als Parameter einer Methode (etwa einer Klasse) oder Funktion (in einem Standard-Modul) zu übergeben? Wenn dieser Versuch nicht vergebens wäre, könnten Sie vom anvisierten Ziel aus über die Eigenschaften, Methoden und Ereignisse des UserDocument-Objekts verfügen - gerade so, wie Sie etwa über ein Form verfügen können, dessen Referenz Sie weitergereicht haben. Damit wir uns nicht missverstehen: Gemeint ist der Zugriff auf das UserDocument-Objekt selbst (zum Beispiel der Zugriff auf UserDocument.BackColor usw.), und nicht auf das Dokument-Objekt, das Sie so gerade programmieren wollen.
Nach ein paar Experimenten werden auch Sie vielleicht darauf gekommen sein, dass die Übergabe funktioniert, wenn der Parameter als Variant deklariert und die Referenz als Wert (ByVal) übergeben wird:
Public Sub TuWas(ByVal UserDocumentObject As Variant)
Nun können Sie auf die Methoden und Eigenschaften des UserDocument-Objekts zugreifen:
UserDocumentObject.BackColor = vbRed
End Sub
Sogar das Ablegen der so übergebenen Referenz auf das UserDocument-Objekt zur weiteren Verwendung in einer lokalen Objekt-Variablen des Datentyps Object funktioniert:
Private mUserDocument As Object
Public Sub StoreUserDocument(ByVal UserDocumentObject As Variant)
Set mUserDocument = UserDocumentObject
End Sub
Public Sub TuWas()
mUserDocument.BackColor = vbCyan
End Sub
Immerhin können wir so mit der Referenz einiges anfangen, auch wenn wir sie so leider nur in so genannter "später" Bindung verwenden können.
Damit bleibt nun noch ein Punkt zu knacken: die Deklaration der Objekt-Variablen "As UserDocument". Damit würden wir zum einen die so genannte "frühe" (und wesentlich schnellere) Bindung erreichen.
Sie wissen nicht, was es mit dieser frühen oder späten "Pünktlichkeit" der Bindung anscheinend auf sich hat? Okay, entweder gedulden Sie sich, bis ich einmal die Muße finde, darüber einen Artikel zu schreiben. Oder Sie lesen baldmöglichst ein sehr empfehlenswertes Buch und lernen damit auch noch das gesamte Fundament der COM-/ActiveX-Programmierung mit Visual Basic kennen: "com/activex-komponenten...".
Nun, weiter im Kontext: Zum anderen könnten wir aus der lokalen Objekt-Variablen auch einen Ereignisempfänger machen. Wie ich im Artikel Lausch-Eingriffe gezeigt habe, können Sie ohne weiteres über eine als Ereignisempfänger per WithEvents ... As Form deklarierte Objekt-Variable die Ereignisse eines Forms außerhalb desselben abfangen. Doch der analoge Versuch, das gleiche mit einem UserDocument-Objekt zu veranstalten (WithEvents ... As UserDocument), geht schief.
Das heißt, nicht ganz - bei der Deklaration der Objekt-Variablen macht uns Visual Basic nämlich noch keinen Strich durch die Rechnung. Aber beim Versuch der Zuweisung der doch so elegant übergebenen Referenz bekommen wir wieder eine frustrierende Fehlermeldung vor die Nase gesetzt: "Laufzeitfehler 13 - Typen unverträglich". Ich habe wohl so ziemlich alle Möglichkeiten durchgespielt - Deklarationen variiert, mit Konvertierfunktionen und deren Parametern herumgespielt, mit ByVal und ByRef... - eine letztlich so simple Möglichkeit wie das obenstehende "ByVal As Variant" scheint es hierfür nicht zu geben - oder kennen Sie zufällig eine? Dann nichts wie her damit...!
Schließlich habe ich aber doch noch eine Lösung gefunden. Sie beruht darauf, die übergebene Objekt-Referenz per Zeiger-Kopie einer als UserDocument deklarierten Objekt-Variablen unterzujubeln und damit zugleich von allem Ballast, der zuvor bei unseren Versuchen die VB-Alarmglocke schrillen ließ, zu befreien. Dazu verwenden wir die API-Funktion RtlMoveMemory (die üblicherweise wie auch sinnfälligerweise per Alias-Deklaration in CopyMemory umgetauft wird) und die undokumentierte VB-Funktion ObjPtr, die den Referenz-Zeiger auf eine Objekt-Variable liefert. Dieses Kopier-Verfahren wird in der COM-Programmierung des öfteren genutzt, wenn mit nicht gerade VB-freundlichen bzw. nicht VB-konformen COM-Schnittstellen und Typbibliotheken hantiert wird. Erwarten Sie aber bitte nicht, dass ich hier weiter darauf eingehe - das würde an dieser Stelle zu weit führen.
Jedoch habe ich, wie immer, für Sie die notwendigen Code-Zeilen in eine Hilfsfunktion gepackt. Damit wir unser Gedächtnis nicht überstrapazieren, wenn wir die Funktion benötigen, habe ich sie CUDoc genannt - ganz auf der Linie von CInt, CVar, CDate und dergleichen.
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(VarDest As Any, VarSource As Any, ByVal BytesToCopy As Long)
Public Function CUDoc(ByVal iUDoc As Variant) As UserDocument
Dim nUDoc As UserDocument
CopyMemory nUDoc, ObjPtr(iUDoc), 4
Set CUDoc = nUDoc
CopyMemory nUDoc, 0&, 4
End Function
Das Belauschen der inneren Ereignisse eines UserDocuments in einer Klasse, einem auf dem UserDocument platzierten Steuerelement (UserControl) oder in anderen UserDocument geht nun wie gewohnt und fast wie von selbst vonstatten. In einer lauschenden Klasse (etwa namens cHookUserDocument) sähe der Code beispielsweise so aus:
Private WithEvents eUserDocument As UserDocument
Public Sub Init(UserDocumentObject As UserDocument)
Set eUserDocument = UserDocumentObject
End Sub
Private eUserDocument_Click()
MsgBox "Extern abgefangener Click auf ein UserDocument"
End Sub
Und im UserDocument konvertieren Sie das UserDocument-Objekt mit CUDoc, bevor Sie die Referenz an eine Klasse nach dem obenstehenden Muster übergeben:
Private pHookUDoc As cHookUserDocument
Private Sub UserDocument_ReadProperties(PropBag As PropertyBag)
Set pHookUDoc = New cHookUserDocument
pHookUDoc.Init CUDoc(UserDocument)
End Sub
Das gleiche Verfahren können Sie übrigens auch für das UserControl-Objekt verwenden.
|