VB-Schlüsselwörter als öffentliche Methoden verwenden

Veröffentlicht: 09. Mrz 2004 | Aktualisiert: 29. Jun 2004

Von Mathias Schiffer

In diesem Artikel erfahren Sie, wie Sie unter Visual Basic 6.0 ein reserviertes Schlüsselwort als Namen einer öffentlichen Funktion in einer COM-DLL verwenden (z.B. "For").

Auf dieser Seite

 Aus der Visual Basic Newsgroup microsoft.public.de.vb:
 Beispielhaftes Vorgehen
 Dekompilieren der Typbibliothek
 Erzeugen der neuen Typbibliothek
 Ersetzen der Typbibliothek
 Da fehlt doch noch etwas? Klar: Late Binding!

Die Microsoft Newsgroups sind eine Quelle schier unerschöpflichen Wissens, das nahezu auf Knopfdruck abrufbar ist: Hunderte deutschsprachige Entwickler vom Einsteiger bis zum Profi treffen sich hier täglich virtuell, um Fragen zu stellen oder zu beantworten. Auch themennahe Probleme, Ansichten und Konzepte werden miteinander diskutiert. Sie kennen die Microsoft Newsgroups noch nicht? Detaillierte Information für Ihre Teilnahme finden Sie auf der Homepage der Microsoft Support Newsgroups.

Diese Kolumne greift regelmäßig ein besonders interessantes oder häufig nachgefragtes Thema aus einer der Entwickler-Newsgroups auf und arbeitet es aus.

Aus der Visual Basic Newsgroup microsoft.public.de.vb:

Ich versuche unter Visual Basic 6.0, ein reserviertes Schlüsselwort als Namen einer öffentlichen Funktion in einer COM-DLL zu verwenden (z.B. "For"). VB akzeptiert das Schlüsselwort aber so nicht und verweigert die Kompilierung. Was kann ich tun?

Bestimmte Schlüsselwörter sind unter Visual Basic fest an die Sprache vergeben und daher nicht ohne weiteres als Namen für Funktionen etc. verwendbar. Das gilt nicht nur, aber insbesondere auch für öffentliche Funktions- und Methodennamen.

In Hinblick auf öffentliche Methoden gelten aber - eigentlich - ganz andere Regeln: Hier bestimmt COM die Grenzen Ihrer Möglichkeiten. Allerdings summieren sich die Grenzen durch Regeln von VB und von COM natürlich auf, wenn Sie Visual Basic als Werkzeug zur Erstellung eines COM-Servers verwenden.

Es gibt dennoch eine Möglichkeit, das gewünschte Ziel zu erreichen. Allerdings werden wir hierbei die Sicherungssysteme von Visual Basic nicht nur überlisten, sondern sogar absichtlich hintergehen. Weniger erfahrene VB-Entwickler sollten bei diesem Vorgehen mit äußerster Umsicht vorgehen, am besten sogar auf dieses Ansinnen verzichten und das reservierte Schlüsselwort schlicht meiden. Doch auch der Profi sollte nur im Ausnahmefall - und selbst dann noch sehr aufmerksam - mit dieser Möglichkeit umgehen. Was ich Ihnen in diesem Artikel vorstellen werde, liegt abseits des Alltagspfades.

Um die Idee realisieren zu können gehen Sie wie folgt vor: Erstellen Sie Ihren COM-Server wie gewohnt. Öffentliche Funktions-, Prozedur- oder Parameter-Namen, die als Visual Basic-Schlüsselwörter gesperrt sind, ersetzen Sie durch gleich lange, bitte eindeutig zuzuordnende und zudem einfach erkennbare Begriffe. Etwa "Prin_" anstelle von "Print". Visual Basic lässt das Kompilieren dieser Version dann problemlos zu. Bis hierher hat sich kein Unterschied zum normalen Vorgehen gezeigt - Sie haben lediglich andere Namen in Ihrer öffentlichen Schnittstelle genutzt.

Als nächsten Schritt werden wir den soeben von Visual Basic erzeugten COM-Server in seine Bestandteile zerlegen und verändern. Wir verlassen damit den Pfad der Tugend - bitte erwarten Sie nicht, dass Sie auf offiziellen Wegen Support für dieses Vorgehen oder Folgeprobleme erhalten. Fühlen Sie sich auf sich allein gestellt - wenn das für Sie nichts ist, dann lesen Sie bis hier her und nicht weiter.

Sie haben weiter gelesen? Sehr gut: Willkommen in der Wildnis und nichts wie heran an den Speck, unerschrockene Entwickler!

Es ist zuerst enorm wichtig, dass Sie sich vom derzeitigen Stand Ihres COM-Servers eine Sicherheitskopie anlegen. Für folgende Versionen Ihres Servers, die Sie rückwärts-, also binärkompatibel erstellen wollen, wird diese Version als Kompatibilitätsquelle dienen. Auch zukünftige Versionen erstellen Sie im Rahmen von VB immer mit den "falschen" Methodennamen und kompilieren sie wie gewohnt. Erst danach fahren Sie fort, wie hier beschrieben.

 

Beispielhaftes Vorgehen

Beginnen wir von Anfang an mit einem beispielhaften COM-Server: Wir schreiben eine ActiveX-DLL, die eine Methode "Print" und eine Methode "For" veröffentlichen soll. Klar: Da "Print" und "For" von VB reservierte Schlüsselworte sind, laufen wir voll in das zu lösende Problem hinein. Wie vorgeschlagen, nennen wir die eine Methode daher "Prin_", die andere "Fo_". So einfach sieht das Grundgerüst der einzigen Klasse des ActiveX-DLL-Projekts aus:

Option Explicit 
' Umbenennen des Projekts in: MyLibrary 
' Umbenennen der Klasse in: MyObject 
Public Sub Prin_(ByVal Copies As Long) 
  MsgBox CStr(Copies) & " Kopien gedruckt!" 
End Sub 
Public Sub Fo_(ByVal Times As Long) 
  MsgBox CStr(Times) & " Wiederholungen simuliert." 
End Sub

Erzeugen Sie aus diesem Code die DLL MyLibrary.dll (sagen wir: in einem Verzeichnis c:\MyLibrary) und schließen Sie Visual Basic 6 wieder, nachdem Sie die Projektdateien gespeichert haben. Legen Sie jetzt sofort auch Ihre Sicherheitskopie an! Wir nennen Sie hier MyLibrary.001 und legen sie an sicherem Ort ab. Bei Bedarf wird sie später wieder umbenannt und reaktiviert (im Rahmen dieses Artikels allerdings nicht mehr).

Wir benötigen im weiteren Verlauf drei Tools: Einen ordentlichen Ressourcen-Editor, den "OLE/COM Object Viewer" (den finden Sie bei Bedarf auf Ihrer Visual Basic CD oder hier als Microsoft-Download) sowie das Kommandozeilen-Utility MIDL.EXE aus dem Platform SDK. Praktisch wäre ein Hex-Editor, den wir aber alternativ durch ein Stück Code unnötig machen werden.

 

Dekompilieren der Typbibliothek

Starten Sie zuerst den "OLE/COM Object Viewer" und wählen Sie über das Menü File - View TypeLib die soeben erzeugte DLL-Datei aus. Sie sollten danach die dekompilierte Typbibliothek Ihres COM-Servers im rechten Fenster sehen können.

news

Bild 1.

Kopieren Sie diesen Text in einen Editor (etwa Notepad.exe) und speichern Sie den Text im Verzeichnis der DLL als Datei "MyLibrary.txt". Die Beschreibung der Schnittstelle sollte dann folgendermaßen aussehen:

// Generated .IDL file (by the OLE/COM Object Viewer) 
//  
// typelib filename: MyLibrary.dll 
[ 
  uuid(61756FF5-A265-4307-BDC0-718829E16A84), 
  version(1.0) 
] 
library MyLibrary 
{ 
 // TLib :  // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046} 
 importlib("stdole2.tlb"); 
 // Forward declare all types defined in this typelib 
 interface _MyObject; 
 [ 
   odl, 
   uuid(1FB49F5E-A7FE-4C74-9C45-C71BCF2DD1F1), 
   version(1.0), 
   hidden, 
   dual, 
   nonextensible, 
   oleautomation 
 ] 
 interface _MyObject : IDispatch { 
  [id(0x60030000)] 
  HRESULT Prin_([in] long Copies); 
  [id(0x60030001)] 
  HRESULT Fo_([in] long Times); 
 }; 
 [ 
   uuid(B304B4B9-9BB3-4BBC-BA1D-958390C7A51E), 
   version(1.0) 
 ] 
 coclass MyObject { 
  [default] interface _MyObject; 
 }; 
};

Die umbenannten Methoden "Prin_" und "Fo_" tauchen hier auf als "HRESULT Prin_([in] long Copies);" und "HRESULT Fo_([in] long Times);". Nennen Sie die Methoden wieder "Print" und "For", so dass sich ergibt: "HRESULT Print([in] long Copies);" und "HRESULT For([in] long Times);". Speichern Sie die Textdatei nach dieser Änderung und verlassen Sie den Editor.

 

Erzeugen der neuen Typbibliothek

Die so geänderte Schnittstellenbeschreibung für Ihren COM-Server muss nun wieder in eine binäre Typbibliothek kompiliert werden. Hierfür verwenden Sie das Kommandozeilen-Utility MIDL.EXE. Als Argument übergeben Sie einfach den Pfad zur eben abgespeicherten Textdatei:

MIDL c:\MyLibrary\MyLibrary.txt /out c:\MyLibrary

Nach erfolgreicher Abarbeitung wurde im gleichen Verzeichnis die Datei MyLibrary.tlb erzeugt - die neu kompilierte Typbibliothek für Ihren COM-Server. Nun muss dieser benachrichtigt werden.

 

Ersetzen der Typbibliothek

Um die neue Typbibliothek nutzen zu können, verwenden wir einen Ressourcen-Editor, in dem die DLL-Datei geöffnet wird. Dabei sollte sich eine Ressource vom Typ "TYPELIB" als Ressourcennummer 1 zeigen.

Für den nächsten Schritt benötigen Sie einen Ressourcen-Editor, der die Ressourcen einer EXE- oder DLL-Datei öffnen und ändern kann. Wir verwenden hier Visual C++ 6.0. Sie können aber auch jedes andere Tool verwenden, das eingebettete Ressourcen beherrscht.

Newsgroups0204

Bild 2.

Nach Markierung der vorhandenen Typbibliotheks-Ressource "TYPELIB - 1 [Neutral]" drücken Sie auf die Entfernen-Taste, um die bisherige Ressource zu löschen. Über das Einfügen-Menü (oder einen entsprechenden Befehl des von Ihnen genutzten Ressourcen-Editors) können Sie die selber neu kompilierte Typbibliothek MyLibrary.tlb als erste Ressource vom Typ "TYPELIB" hinzufügen. Nachdem Sie die Änderung gespeichert haben, können Sie den Ressourcen-Editor wieder beiseite legen.

Newsgroups0204_03

Bild 3.

Nun können Sie die von Ihnen in der Bibliothek MyLibrary definierten Methoden Print und For bereits von COM-Clients aus verwenden. Der Beweis: Referenzieren Sie die abgeänderte DLL in Visual Basic 6.0 als Verweis und betrachten Sie als Ergebnis zunächst den Objektkatalog:

Newsgroups0204_

Bild 4.

Auch eine Verwendung der For-Methode bereitet zumindest bei Early Binding keine Probleme:

Dim MyObj As MyLibrary.MyObject 
  Set MyObj = New MyLibrary.MyObject 
  Call MyObj.For(42)

Wenn Sie im Beispielcode den Aufruf der Print-Methode vermissen: Ein Aufruf würde funktionieren - glauben Sie es mir. Allerdings kann Visual Basic trotz allem mit der Print-Methode nichts anfangen. Das aber liegt ausschließlich an Visual Basics Parser und ist historisch bedingt: Die Argumentmöglichkeit der althergebrachten Print-Methode aus Microsoft BASIC lässt sich guten Gewissens in keine objektorientierte Version übertragen. Deshalb "pfuscht" der Parser von Visual Basic ganz erheblich am VB-eigenen Prinzip der COM-Basiertheit vorbei, wenn im Sourcecode eine Print-Methode auftaucht. Solch filigranes Machwerk kann bei unorthodoxen Eingriffen wie dem unseren nur schief gehen - und genau das passiert auch (wenigstens auf etwas kann man sich verlassen ;-). Andere COM-Clients, die weniger vorbelastet durch die Print-Methode sind, schaffen den Zugriff. Probieren Sie es ruhig aus!

 

Da fehlt doch noch etwas? Klar: Late Binding!

Versuchen Sie, auf die geänderte Variante per Late Binding zuzugreifen, so werden Sie derzeit noch scheitern:

Dim MyObj As Object 
  Set MyObj = CreateObject("MyLibrary.MyObject") 
  Call MyObj.For(42)

Zur zusätzlichen Unterstützung von Late Binding - vital wichtig auch für Scripting Clients wie etwa den "Windows Scripting Host" - ist noch eine binäre Änderung an der DLL-Datei notwendig.

Wenn Sie einen Hex-Editor besitzen, öffnen Sie einfach die DLL-Datei und ersetzen Sie die Einträge "Prin_" und "Fo_" noch durch die entsprechenden Ersatzstücke. Achtung: Falls es jeder Begriff häufiger als einmal vorkommen sollte, ist Ihnen entweder im vorherigen Ablauf ein grober Fehler unterlaufen oder die von Ihnen gewählten Begriffe waren nicht eindeutig genug. Nebenbei: Auch in einem solchen Fall werden Sie sich zu einer vorliegenden Sicherheitskopie beglückwünschen!

Speichern Sie die erfolgreich binär geänderte Version der DLL, klappt ab sofort das Late Binding!

Natürlich benötigen Sie für den letzten Schritt nicht zwingend einen Hex-Editor. Ein VB-konformes Einlesen der Datei, Ersetzen der gesuchten Ausdrücke und Abspeichern der Datei funktioniert auch. Ein kurzes Codebeispiel hierfür zum krönenden Abschluss:

Private Sub LateBindingReplacements() 
Dim ff   As Integer 
Dim strFile As String 
Dim strOld  As String 
Dim strNew  As String 
Dim lLen As Long 
  ' Betroffene DLL-Datei binär öffnen und einlesen 
  ff = FreeFile() 
  Open "javascript:void(null);" For Binary Access Read As #ff 
 strFile = Space$(LOF(ff)) 
 Get #ff, , strFile 
  Close #ff 
  ' Erste Ersetzung definieren 
  strOld = "Prin_" 
  strNew = "Print" 
  lLen = Len(strFile) 
  ' Prüfen, ob nur eine Ersetzung zutreffend ist (notwendig) 
  If (lLen - Len(Replace(strFile, strOld, vbNullString)) <> Len(strOld)) Then 
 MsgBox "Die Datei enthält den zu ersetzenden Begriff " & _ 
  strOld & " nicht exakt einmal. Bitte wählen Sie " & _ 
  " eine eindeutigere Namensverschleierung!" 
 Exit Sub 
  End If 
  ' Ersetzen 
  strFile = Replace(strFile, strOld, strNew) 
  ' Zweite Ersetzung definieren 
  strOld = "Fo_" 
  strNew = "For" 
  lLen = Len(strFile) 
  ' Prüfen, ob nur eine Ersetzung zutreffend ist (notwendig) 
  If (lLen - Len(Replace(strFile, strOld, vbNullString)) <> Len(strOld)) Then 
 MsgBox "Die Datei enthält den zu ersetzenden Begriff " & _ 
  strOld & " nicht exakt einmal. Bitte wählen Sie " & _ 
  " eine eindeutigere Namensverschleierung!" 
 Exit Sub 
  End If 
  ' Ersetzen 
  strFile = Replace(strFile, strOld, strNew) 
  ' Weitere Ersetzungen definieren 
  ' [...] 
  ' Die geänderte Datei schreiben 
  ff = FreeFile() 
  Open "javascript:void(null);" For Binary Access Write As #ff 
 Put #ff, , strFile 
  Close #ff 
End Sub