VBA-Editor: Quellcode-Bearbeitung automatisieren

Möchtest Du den gesamten Artikel lesen? Und vielleicht sogar den Artikel im PDF-Format und die Beispieldateien herunterladen? Dann hole Dir den Artikel gleich hier - völlig kostenlos!

Der VBA-Editor ist die Gemeinsamkeit der Office-Anwendungen wie Access, Excel, Outlook, PowerPoint und Word. Wer eine oder mehrere dieser Anwendungen programmiert, um dem Benutzer die Arbeit damit zu erleichtern, kennt sich mehr oder weniger mit VBA aus. In der Regel wird im Arbeitsalltag eines Entwicklers jede Codezeile von Hand neu programmiert. Gegebenenfalls kopiert man bestehenden Code und passt diesen an den jeweiligen Anwendungszweck an. Aber warum nicht einen Schritt weitergehen und VBA-Code zur Erstellung von VBA-Code selbst nutzen? Visual Basic bietet eigens zum Zweck der Programmierung der Elemente und des Codes im VBA-Editor eine eigene Bibliothek namens Microsoft Visual Basic for Applications Extensibility 5.3. Welche Möglichkeiten diese allgemein bietet und welche Elemente, Eigenschaften und Methoden sie bereitstellt, schauen wir uns in diesem Artikel an.

Der VBA-Editor ist die Entwicklungsumgebung für die Programmierung der verschiedenen Office-Anwendungen und der darin enthaltenen Dokumente und Elemente. Er zeigt im Projekt-Explorer die programmierbaren Elemente wie Standardmodule und Klassenmodule an und erlaubt es, den Code dieser Elemente im Code-Editor anzuzeigen und zu bearbeiten.

Die Codeeingabe selbst ist bis auf wenige Hilfestellungen durch IntelliSense Aufgabe des Entwicklers – hier ist viel Tipparbeit angezeigt oder gegebenenfalls noch das Kopieren und Anpassen bereits vorhandenen Codes.

IntelliSense unterstützt uns allerdings auch nur dabei, den Datentyp für Variablen auszuwählen oder die Eigenschaften oder Methoden von Objektvariablen zu selektieren. Wenn wir mehr aus der Entwicklungsumgebung herausholen wollen, müssen wir uns also selbst helfen.

Dabei können wir bereits mit den üblichen Mitteln von VBA Einiges erreichen: So können wir die Zeichenkettenfunktionen in Kombination mit Schleifen nutzen, um bestimmte Abfolgen von Anweisungen im Direktbereich des VBA-Editors auszugeben, die wir dann kopieren und in unseren Code einfügen.

Wir können jedoch noch viel mehr erreichen, wenn wir eine Bibliothek namens Microsoft Visual Basic for Applications Extensibility 5.3 einbinden. Diese fügen wir über den Verweise-Dialog, den wir mit dem Menübefehl Extras|Verweise öffnen, zum aktuellen VBA-Projekt hinzu. Dazu versehen wir den Eintrag wie in Bild 1 mit einem Haken.

Setzen eines Verweises auf die Bibliothek Microsoft Visual Basic for Applications Extensibility 5.3

Bild 1: Setzen eines Verweises auf die Bibliothek Microsoft Visual Basic for Applications Extensibility 5.3

Die Klassen und Elemente der VBE-Bibliothek

Anschließend können wir uns im Objektkatalog, zu öffnen mit der Taste F2, die Klassen und Enumerationen dieser Bibliothek ansehen (siehe Bild 2).

Die Elemente der Bibliothek Microsoft Visual Basic for Applications Extensibility 5.3

Bild 2: Die Elemente der Bibliothek Microsoft Visual Basic for Applications Extensibility 5.3

Damit nur die Elemente der hier VBIDE genannten Bibliothek angezeigt werden, haben wir diesen Eintrag im Kombinationsfeld ganz oben ausgewählt.

Danach sehen wir in der linken Liste alle Klassen und Enumerationen dieser Bibliothek und rechts die Elemente des aktuell markierten Eintrags der linken Liste, hier der Hauptklasse VBE.

Und hier ist eine kurze Beschreibung der Elemente, die wir direkt über die Hauptklasse erreichen können:

  • ActiveCodePane: Aktueller Codebereich. Codebereich ist erklärungsbedürftig, darauf gehen wir weiter unten ein.
  • ActiveVBProject: Aktuelles VBA-Projekt. Ermittelt das aktive von gegebenenfalls mehreren aktuell geöffneten VBA-Projekten im VBA-Editor.
  • ActiveWindow: Aktuelles Fenster. Zu den Fenstern zählen nicht nur die Codefenster, sondern auch der Direktbereich, der Projekt-Explorer et cetera.
  • Addins: Auflistung der COM-Add-Ins, die aktuell für den VBA-Editor registriert sind.
  • CodePanes: Auflistung aller CodePane-Elemente.
  • CommandBars: Auflistung aller CommandBar-Elemente, also aller Menüleisten des VBA-Editors.
  • Events: Auflistung aller Event-Auflistungen, enthält die beiden Auflistungen CommandBarEvents und ReferencesEvents.
  • MainWindow: Verweis auf das Hauptfenster des VBA-Editors
  • SelectedVBComponent: Verweis auf die aktuell im Projekt-Explorer selektierte Komponente, also beispielsweise ein Modul
  • VBProjects: Auflistung aller VBA-Projekte, die aktuell im Projekt-Explorer angezeigt werden.
  • Version: Version von VBA, aktuell 7.01
  • Windows: Auflistung aller Fenster des VBA-Editors. Dabei werden auch einige nicht geöffnete Fenster aufgelistet.

Deklarieren und Referenzieren

Beim Deklarieren der Elemente der VB-Editor-Bibliothek können wir die übergeordnete Klasse VBIDE verwenden. Damit erhalten wir dann alle Elemente dieser Bibliothek per IntelliSense:

Dim objWindow As VBIDE.Window

Bei Referenzieren nutzen wir hingegen einfach VBE, zum Beispiel:

Set objWindow = VBE.ActiveWindow

Fenster im VBA-Editor programmieren

Die Fenster im VBA-Editor können wir mit der Windows-Auflistung durchlaufen. Dabei verwenden wir als Laufvariable der For Each-Schleife eine Variable des Typs Window:

Public Sub FensterDurchlaufen()
     Dim objWindow As VBIDE.Window
     For Each objWindow In VBE.Windows
         With objWindow
             Debug.Print .Type, .WindowState, .Caption
         End With
     Next objWindow
End Sub

Hier geben wir den Typ, den Fensterstatus und die Beschriftung aus, was bei einem geöffneten Modul wie in Bild 3 aussieht.

Ausgabe der Fenster mit einigen Eigenschaftswerten

Bild 3: Ausgabe der Fenster mit einigen Eigenschaftswerten

Was die einzelnen Zahlen für die Eigenschaft Type bedeuten, können wir der Enumeration vbext_WindowType entnehmen, die wir im Objektkatalog finden. 0 steht beispielsweise für vbext_wt_CodeWindow. Die anderen Werte stehen für die jeweiligen Fenstertypen wie Direktbereich, Projekt-Explorer und so weiter.

Wenn wir einmal die Fenster und ihre Position steuern wollen, können wir diese und andere Eigenschaften der Window-Objekte nutzen. Diese liefern beispielsweise auch die Position und Größe der Fenster.

VBA-Projekte

Der VBA-Editor kann durchaus einmal mehrere VBA-Projekte gleichzeitig im Projekt-Explorer anzeigen, beispielsweise wenn aktuell ein Add-In geladen ist.

Diese können wir über die Auflistung VBProjects durchlaufen:

Public Sub ProjekteDurchlaufen()
     Dim objVBProject As VBIDE.VBProject
     For Each objVBProject In VBE.VBProjects
         Debug.Print objVBProject.Name
     Next objVBProject
End Sub

Das Element VBProject liefert neben dem Namen noch einige weitere interessante Eigenschaften:

  • Filename: Liefert den Namen der Datei, in dem sich das VBA-Modul befindet. Das kann hilfreich sein, um das VBA-Modul zum aktuell geöffneten Dokument zu ermitteln.
  • VBComponents: Liefert eine Auflistung aller VBComponent-Elemente, also der VBA-Module.

Aktuelles VBA-Projekt ermitteln

Wenn mehrere VBA-Projekte geladen sind, liefert ActiveVBProject abhängig von dem Ort, wo es ausgeführt wird, unterschiedliche Ergebnisse. Wenn wir es im Direktbereich aufrufen, erhalten wir einen Verweis auf das VBA-Projekt, das gerade im Projekt-Explorer den Fokus hat. Wenn wir es aus einem Modul heraus aufrufen, erhalten wir mit ActiveVBProject einen Verweis auf das VBA-Projekt, in dem sich das aufrufende Modul befindet.

VBA-Projekt zu einem bestimmten Dokument ermitteln

Manchmal kann es interessant sein, genau das VBA-Projekt zu ermitteln, das zu einem bestimmten Dokument oder einer Datenbank gehört. Voraussetzung dafür ist, dass wir den Pfad des aktuellen Dokuments kennen. Dann können wir einfach die Liste der VBA-Projekte im VBA-Editor durchlaufen und prüfen, ob der mit der Eigenschaft Filename gelieferte Pfad mit dem Pfad des Dokuments übereinstimmt.

Die VBA-Komponten oder: VBComponents

Die Auflistung VBComponents des VBProject-Objekts liefert alle VBComponent-Elemente des VBA-Projekts. Im folgenden Beispiel durchlaufen wir alle Elemente und lassen uns den Namen und den Typ ausgeben:

<b>Public Sub KomponentenDurchlaufen()
<b>    Dim objVBProject As VBIDE.VBProject
<b>    Dim objVBComponent As VBIDE.VBComponent
<b>    Set objVBProject = VBE.ActiveVBProject
<b>    For Each objVBComponent In objVBProject.VBComponents
<b>       Debug.Print objVBComponent.Name, objVBComponent.Type
<b>    Next objVBComponent
End Sub

Die verschiedenen Typen können wir der Enumeration vbext_ComponentType entnehmen. Hier finden wir:

  • 11: vbext_ct_ActiveXDesigner
  • 2: vbext_ct_ClassModule
  • 100: vbext_ct_Document
  • 3: vbext_ct_MSForm
  • 1: vbext_ct_StdModule

Neue VB-Komponente anlegen

Für das Bearbeiten von VBA-Projekten kann es hilfreich sein, neue Module anzulegen.

Module sind VBComponent-Objekt, also nutzen wir dazu die Methode Add der VBComponents-Auflistung.

Dieser übergeben wir für ein neues Standardmodul den Wert vbext_ct_StdModule als Parameter und referenzieren das neue Modul mit der Variablen objVBComponent. Diesem geben wir anschließend direkt einen neuen Namen:

Public Sub NeuesModul()
     Dim objVBComponent As VBIDE.VBComponent
     Set objVBComponent = VBE.ActiveVBProject. _
         VBComponents.Add(vbext_ct_StdModule)
     objVBComponent.Name = "mdlNeuesModul"
End Sub

Würden wir beispielsweise ein Klassenmodul erstellen wollen, ginge das mit folgender Anpassung:

Set objVBComponent = VBE.ActiveVBProject. _
     VBComponents.Add(vbext_ct_ClassModule)
objVBComponent.Name = "clsNeueKlasse"

Von der Komponente zum Modul

Mit dem neuen VBComponent-Objekt können wir noch nicht allzu viel anfangen, wenn wir seinen Code anpassen wollen. Dazu müssen wir das CodeModule-Objekt referenzieren, dass sich hinter der gleichnamigen Eigenschaft des VBComponent-Elements verbirgt.

CodeModule referenzieren

Es gibt allerdings verschiedene Wege, ein CodeModule-Element zu referenzieren. Der erste ist der Weg über das VBComponent-Element, in dem sich das Modul befindet. Wenn wir den Namen des VBComponent-Elements kennen, gelingt das beispielsweise wie folgt:

Public Sub MitModulArbeiten()
     Dim objCodeModule As VBIDE.CodeModule
     Set objCodeModule = VBE.ActiveVBProject. _
         VBComponents("mdlNeuesModul").CodeModule
     Debug.Print objCodeModule.Lines(1, 10)
End Sub

Um zu prüfen, ob wir das richtige CodeModule-Element referenziert haben, geben wir die mit der Line-Methode ermittelten ersten zehn Zeilen des Moduls im Direktbereich des VBA-Editors aus.

Wenn wir wissen, dass das VBComponent-Element mit dem gewünschten CodeModule aktiviert ist, können wir noch schneller darauf zugreifen:

Public Sub MitMarkiertemModulArbeiten()
     Dim objCodeModule As VBIDE.CodeModule
     Set objCodeModule = VBE.ActiveCodePane.CodeModule
     Debug.Print objCodeModule.Lines(1, 10)
End Sub

Hier nutzen wir ein Objekt, das wir noch nicht vorgestellt haben, auf das wir aber über die Eigenschaft ActiveCodePane zugreifen können – das CodePane-Objekt. Auf dieses Objekt kommen wir gleich im Anschluss zu sprechen.

Wichtig ist, dass dieses genau wie VBComponent ebenfalls die Eigenschaft CodeModule bietet, mit der wir auf das CodeModule-Element zugreifen können.

Mit dem Code arbeiten

Die CodeModule-Klasse bietet uns den Großteil der Möglichkeiten, um mit dem Code zu arbeiten – einige weitere finden wir noch im CodePane-Element.

Wir schauen uns nun zunächst die Möglichkeiten von CodeModule an und kommen dann auf CodePane zu sprechen.

  • AddFromFile: Fügt den Inhalt der als Parameter angegebenen Textdatei hinten an den bestehenden Code des Moduls an.
  • AddFromString: Fügt den als Parameter angegebenen Inhalt hinten an den bestehenden Code des Moduls an.
  • CodePane: Verweist auf das CodePane-Objekt, das zu diesem CodeModule-Element gehört.
  • CountOfDeclarationLines: Liefert die Anzahl der Deklarationszeilen des Moduls.
  • CountOfLines: Liefert die gesamte Anzahl der Zeilen des Moduls.
  • CreateEventProc: Erstellt eine Ereignisprozedur und erwartet den Namen des Ereignisses und des Objekts, für das die Prozedur angelegt werden soll.
  • DeleteLines: Löscht die mit dem zweiten Parameter angegebene Anzahl Zeilen von der mit dem ersten Parameter angegebenen Startzeile aus.
  • Find: Sucht in einem bestimmten Bereich des Codes nach einem Text.
  • InsertLines: Fügt in der mit dem ersten Parameter angegebenen Zeile den Text aus dem zweiten Parameter ein.
  • Lines: Liefert die mit dem zweiten Parameter angegebene Anzahl Zeilen beginnend mit der Zeile aus dem ersten Parameter zurück.
  • ProcBodyLine: Liefert die Zeile zurück, in welcher der Body der angegebenen Prozedur beginnt. Der erste Parameter erwartet den Prozedurnamen, der zweite den Prozedurtyp.
  • ProcCountLines: Liefert die Anzahl der Zeilen der angegebenen Prozedur zurück.
  • ProcOfLine: Liefert die Prozedur zurück, in der sich die Zeile mit der im Parameter angegebenen Zeilennummer befindet.
  • ProcStartLine: Gibt die Nummer der ersten Zeile der angegebenen Prozedur zurück.
  • ReplaceLine: Ersetzt die Zeile mit der Zeilennummer aus dem ersten Parameter mit der im zweiten Parameter angegebenen Zeile.

Ausprobieren der Eigenschaften und Methoden der CodeModule-Klasse

Wenn wir die Eigenschaften und Methoden der CodeModule-Klasse ausprobieren wollen, benötigen wir optimalerweise immer ein neues, leeres Modul. Damit wir den gleichen Code zum Erstellen dieses Moduls nicht immer wieder in die Beispielprozeduren schreiben müssen, haben wir zu diesem Zweck eine Funktion erstellt (siehe Listing 1).

Public Function NeuesLeeresModul(strModulname As String) As VBComponent
     Dim objVBProject As VBIDE.VBProject
     Dim objVBComponent As VBIDE.VBComponent
     Set objVBProject = VBE.ActiveVBProject
     On Error Resume Next
     Set objVBComponent = objVBProject.VBComponents(strModulname)
     On Error GoTo 0
     If Not objVBComponent Is Nothing Then
         objVBProject.VBComponents.Remove objVBComponent
     End If
     Set objVBComponent = objVBProject.VBComponents.Add(vbext_ct_StdModule)
     objVBComponent.Name = strModulname
     Set NeuesLeeresModul = objVBComponent
End Function

Listing 1: Die Funktion NeuesLeeresModul

Die Prozedur nimmt den Namen des zu erstellenden Moduls mit dem Parameter strModulname entgegen. Sie deklariert ein VBProject– und ein VBComponent-Element. Das VBProject-Element objVBProject füllt es mit einem Veweis auf das aktuelle VBA-Projekt.

Dann deaktiviert es die eingebaute Fehlerbehandlung, um das eventuell bereits vorhandene VBComponent-Element mit dem Namen aus strModulname zu referenzieren.

Ist ein solches vorhanden, ist objVBComponent anschließend mit einem Verweis auf das entsprechende Objekt gefüllt. Dies prüfen wir durch den Vergleich mit Is Nothing. Ist objVBComponent nicht leer, entfernen wir es mit der Remove-Methode aus der Auflistung VBComponents des aktuellen Projekts.

Dann legen wir das Modul mit der Add-Methode der VBComponents-Auflistung erneut an und weisen diesem den Namen aus strModulname zu. Schließlich geben wir den Verweis auf dieses VBComponent-Element an die aufrufende Routine zurück.

Modul anlegen

Mit der oben vorgestellten Funktion NeuesLeeresModul können wir mit zwei Zeilen ein neues Modul erstellen und mit einer CodeModule-Variablen referenzieren:

Public Sub CodeModule_LeeresModul()
     Dim objCodeModule As VBIDE.CodeModule
     Set objCodeModule = _
         NeuesLeeresModul("mdlTest"). _
         CodeModule
End Sub

Das neue Modul wird anschließend direkt geöffnet (siehe Bild 4).

Erstellen eines neuen, leeren Moduls mit zwei Zeilen

Bild 4: Erstellen eines neuen, leeren Moduls mit zwei Zeilen

Code aus einer Datei hinzufügen

Die CodeModule-Klasse bietet zwei Möglichkeiten, schnell Code einzufügen. Die erste ist die AddFromFile-Methode.

Sie fügt den Inhalt der als Parameter angegebenen Textdatei hinten an den bestehenden Code des Moduls an, für dessen CodeModule-Objekt die Methode aufgerufen wurde. Zu Testzwecken haben wir die einfache Textdatei aus Bild 5 erstellt.

Textdatei mit Beispielcode zum Einlesen

Bild 5: Textdatei mit Beispielcode zum Einlesen

Dann rufen wir die folgende Prozedur auf:

Public Sub CodeModule_AddFromFile()
     Dim objCodeModule As VBIDE.CodeModule
     Set objCodeModule = _
         NeuesLeeresModul("mdlTest").CodeModule
     objCodeModule.AddFromFile _
         CurrentProject.Path & "\code.txt"
End Sub

Das Ergebnis sehen wir in Bild 6. Der Text wird also einfach hinten angefügt.

Modul mit eingefügtem Code aus einer Textdatei

Bild 6: Modul mit eingefügtem Code aus einer Textdatei

Code aus einem Literal oder einer Variablen hinzufügen

Die zweite Möglichkeit, Code einfach hinten an den bestehenden Code eines Moduls anzufügen, ist die Methode AddFromString. Sie erwartet ein Literal oder eine Variable, deren Inhalt zum Modul hinzugefügt wird. Im einfachsten Fall geben wir den hinzuzufügenden Code als Parameter an:

Public Sub CodeModule_AddFromString()
     ...
     objCodeModule.AddFromString "Dim strTest As String"
End Sub

Wir können den Code zuvor auch in einer Variablen zusammenstellen (siehe Listing 2). Wichtig sind hier zwei Faktoren:

Public Sub CodeModule_AddFromString_Variable()
     Dim objCodeModule As VBIDE.CodeModule
     Dim strCode As String
     Set objCodeModule = NeuesLeeresModul("mdlTest").CodeModule
     strCode = "Dim strTest As String" & vbCrLf & vbCrLf
     strCode = strCode & "Public Sub Test()" & vbCrLf
     strCode = strCode & "    strTest = ""Beispieltext""" & vbCrLf
     strCode = strCode & "    MsgBox strTest" & vbCrLf
     strCode = strCode & "End Sub"
     objCodeModule.AddFromString strCode
End Sub

Listing 2: Hinzufügen von Code aus einer Variablen

  • Wenn man wie im Beispiel den Code zeilenweise zur Variable strCode hinzufügt muss man hinten immer einen Zeilenumbruch anfügen, in diesem Fall mit der Konstanten vbCrLf – was den Zeichen Chr(13) & Chr(10) entspricht.
  • Der hinzuzufügende Text wird in Anführungszeichen gesetzt. Wenn dieser wiederum Anführungszeichen enthält, muss man diese verdoppeln, damit VBA nicht innerhalb der Zeichenkette vom Ende des Literals ausgeht.

Das Beispiel liefert das gleiche Modul wie das vorherige.

Zeilen zählen

Es gibt verschiedene Funktionen, mit denen wir die Zeilen innerhalb des Moduls zählen können. Zwei stellen wir hier vor, eine weitere zählt die Zeilen einer Prozedur – diese schauen wir uns weiter unten an.

Die Funktion CountOfDeclarationLines liefert die Anzahl der Deklarationszeilen des Moduls. Was bedeutet das genau? Und was sind die Deklarationszeilen?

Die Deklarationszeilen sind alle Zeilen, die sich vor der ersten Sub-, Function– oder Property-Prozedur befunden oder, wenn keine solche Prozedur vorhanden ist, sind es alle Zeilen. Dabei gehören leere Zeilen zwischen der letzten Deklarationszeile und der ersten Zeile einer Prozedur zu der jeweiligen Prozedur.

Zu Demonstrationszwecken haben wir in Bild 7 Zeilennummern zum Modul hinzugefügt.

Modul mit Zeilennummern

Bild 7: Modul mit Zeilennummern

Wenn wir für dieses Modul den Wert von CountOfDeclarationLines im Direktbereich ausgeben wollen, nutzen wir folgenden Code:

Public Sub CodeModule_CountOfDeclarationLines()
     ...
     Debug.Print objCodeModule.CountOfDeclarationLines
End Sub

Das liefert in diesem Fall den Wert 4.

In Bild 8 sehen wir ein Beispiel für ein Modul, das gar keine Prozeduren enthält – dafür aber eine auskommentierte Prozedur und einige folgende Leerzeilen. Welche Zeile markiert hier das Ende des Deklarationsbereichs? Die letzte Zeile des Moduls, die Zeichen enthält. Das ist nicht die Zeile 9, sondern die vorletzte Zeile – in diesem Fall die zwölfte Zeile – denn diese enthält noch den Zeilenumbruch (vbCrLf). Wir können die Einfügemarke auch noch in eine 13. Zeile bewegen, die aber nicht mehr als Zeile gewertet wird, weil sie keine Zeichen enthält.

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar