Zeilennummern per VBA hinzufügen

Wer eine wirklich professionelle Fehlerbehandlung zu seiner Access-Anwendung hinzufügen möchte, kommt nicht um das Anlegen von Zeilennummern herum. Wenn man Zeilennummern festgelegt hat, kann man diese im Falle eines Fehlers mit der nicht dokumentierten Erl-Funktion auslesen. Das heißt, dass wir neben der Fehlernummer und der Fehlerbeschreibung noch auf die Zeilennummer zurückgreifen können, um den Fehler zu lokalisieren. Dazu gehört allerdings auch, dass wir in der Fehlermeldung Informationen über das Modul und die Prozedur unterbringen, in denen der Fehler aufgetreten ist – doch dies lässt sich einfach realisieren. Sehr aufwändig ist es hingegen, alle Prozeduren von umfangreichen Anwendungen mit Zeilennummern zu versehen. Um dies zu implementieren, nutzen wir die Bibliothek “Microsoft Visual Basic for Applications Extensibility 5.3”, die alle Möglichkeiten bietet, um auf die enthaltenen Module zuzugreifen und den Code automatisiert nach unseren Wünschen anzupassen.

Funktion zum Hinzufügen von Zeilennummern

Wichtig für die Fehlerbehandlung sind auch die Zeilennummern. Diese fügen wir mit der Funktion AddLineNumbers zu einer Prozedur hinzu. Diese Prozedur statten wir mit den folgenden Parametern aus:

  • strModule: Name des Moduls, in dem sich die Prozedur befindet
  • strProcedure: Name der Prozedur, die mit Zeilennummern versehen werden soll

Die Prozedur aus Listing 1 referenziert als Erstes das aktuelle VBA-Projekt und die mit strModule angegebene Komponente. Dazu holt sie sich über die CodeModule-Eigenschaft noch das passende CodeModule-Objekt, mit dem wir auf den Code zugreifen können. Mit lngProcBodyLine speichern wir die Zeile, in der die Prozedur aus strProcedure beginnt. Wir wollen nur die eigentlichen Anweisungen der Prozedur mit Zeilennummern versehen, also ermitteln wir die Nummer der ersten Zeile hinter dem Prozedurkopf und die Nummer der letzten Zeile vor der End…-Zeile der Prozedur. Dazu nutzen wir die beiden weiter hinten beschriebenen Funktionen GetFirstStatementLineNumber und GetLastStatementLineNumber.

Public Sub AddLineNumbers(strModule As String, strProcedure As String)
     Dim objVBProject As VBIDE.VBProject
     Dim objVBComponent As VBIDE.VBComponent
     Dim objCodemodule As VBIDE.CodeModule
     Dim intProcType As vbext_ProcKind
     Dim lngProcBodyLine As Long
     Dim lngFirstStatementLineNumber As Long
     Dim lngLastStatementLineNumber As Long
     Dim lngLine As Long
     Dim bolNumbers As Boolean
     Dim strLine As String
     Set objVBProject = VBE.ActiveVBProject
     Set objVBComponent = objVBProject.VBComponents(strModule)
     Set objCodemodule = objVBComponent.CodeModule
     lngProcBodyLine = objCodemodule.ProcBodyLine(strProcedure, intProcType)
     lngFirstStatementLineNumber = GetFirstStatementLineNumber(objCodemodule, lngProcBodyLine)
     lngLastStatementLineNumber = GetLastStatementLineNumber(objCodemodule, lngProcBodyLine)
     For lngLine = lngFirstStatementLineNumber To lngLastStatementLineNumber
         strLine = left(objCodemodule.Lines(lngLine, 1), 6)
         Select Case True
             Case IsNumeric(strLine)
                 bolNumbers = True
                 Exit For
         End Select
     Next lngLine
     If bolNumbers = False Then
         For lngLine = lngFirstStatementLineNumber To lngLastStatementLineNumber
             Select Case True
                 Case left(LTrim(objCodemodule.Lines(lngLine, 1)), 5) = "Case "
                     objCodemodule.ReplaceLine lngLine, Space(6) & objCodemodule.Lines(lngLine, 1)
                 Case IsLabel(objCodemodule.Lines(lngLine, 1))
                 Case Else
                     objCodemodule.ReplaceLine lngLine, Format(lngLine, "00000") & " " & objCodemodule.Lines(lngLine, 1)
             End Select
         Next lngLine
     Else
         MsgBox "Die Prozedur ''" & strProcedure & "'' aus Modul ''" & strModule _
             & "'' scheint bereits Zeilennummern zu enthalten.", vbOKOnly + vbInformation, "Zeilennummern vorhanden"
     End If
End Sub

Listing 1: Prozedur zum Hinzufügen von Zeilennummern zu einer Prozedur

Die Prozedur durchläuft nun in einer ersten For…Next-Schleife alle Zeilen der Prozedur, um eventuell bereits mit Zeilennummern versehene Prozeduren zu erkennen. Findet sie während dieses Durchlaufs in irgendeiner Zeile einen numerischen Wert in den ersten sechs Zeichen, wird die Variable bolNumbers auf True eingestellt und die Schleife verlassen. In diesem Fall legt die Prozedur keine neuen Zeilennummern an, sondern gibt eine entsprechende Meldung aus.

Anderenfalls durchläuft die Prozedur alle Zeilen von der ersten Anweisung bis zur letzten Anweisung der mit Zeilennummern zu versehenden Routine. Dies geschieht in einer For…Next-Schleife mit der Laufvariablen lngLine.

Innerhalb der Schleife prüfen wir in einer Select Case-Bedingung, ob verschiedene Bedingungen den Wert True haben. Dazu weisen wir der Select Case-Anweisung das Kriterium True zu. Der erste der folgenden Case-Zweige, der den Wert True liefert, wird demzufolge angesteuert.

Die erste Bedingung prüft, ob die um führende und folgende Leerzeichen erleichterte aktuelle Zeile mit Case beginnt. Case-Zeilen sind eine der wenigen Zeilen, die nicht mit einer Zeilennummer versehen werden können. Eine Case-Zeile soll genau wie die nummerierten Zeilen um sechs Zeichen nach rechts eingerückt werden, auch wenn diese nicht nummeriert wird. Auf diese Weise bleibt die Einrückung gegenüber den nummerierten Zeilen erhalten.

Um die Zeile um sechs Zeichen einzurücken, verwenden wir die ReplaceLine-Anweisung des CodeModule-Objekts. Diese erwartet als ersten Parameter die Nummer der zu ersetzenden Zeile und als zweiten Parameter den neuen Inhalt der Zeile. Diesen stellen wir aus sechs mit der Space-Funktion ermittelten Leerzeichen und der vorherigen Zeile zusammen. Der zweite Case-Fall untersucht, ob es sich bei der Zeile um eine Sprungmarke handelt. Dazu verwendet sie die IsLabel-Funktion, welche die übergebene Zeile untersucht. Diese Funktion stellen wir später vor. Liefert IsLabel den Wert True, geschieht nichts weiter mit dieser Zeile. Sprungmarken sollten weder nummeriert noch eingerückt werden (letzteres wird ohnehin wieder rückgängig gemacht).

Der Case Else-Zweig tritt für alle weiteren Fälle ein. Dieser sorgt dafür, dass die aktuelle Zeile durch eine neue Zeile ersetzt wird, die aus der aktuellen Zeilennummer im Format 00000, einem Leerzeichen und der ursprünglichen Zeile besteht. Warum als Format 00000? Weil wir davon ausgehen, dass wir keine Module mit mehr als 99.999 Zeilen verwenden (falls doch, können Sie das Format und die Einrückungen so anpassen, dass diese mehr Stellen abdecken).

Wenn wir die Zeilenzahl 123 mit dem Format 00000 einfügen, wird daraus 123 plus zwei folgende Leerzeichen. Aus 1 wird 1 plus vier folgende Leerzeichen. Es erfolgt also unabhängig von der Stellenzahl der Zeilennummer eine konstante Einrückung. Auf diese Weise durchlaufen wir alle Zeilen von der ersten bis zur letzten Anweisung der angegebenen Prozedur.

Funktion zum Hinzufügen von Zeilennummern aufrufen

Um die Funktion AddLineNumbers aufzurufen, verwenden wir beispielsweise den folgenden Befehl:

Call AddLineNumbers("mdlTest", "Test")

Damit fügen wir genau einer Prozedur Zeilennummern hinzu.

Zeilennummern zu allen Prozeduren eines Moduls hinzufügen

Gegebenenfalls wollen wir jedoch nicht nur einer, sondern allen Prozeduren eines Moduls Zeilennummern hinzufügen – oder vielleicht sogar allen Modulen des VBA-Projekts. Dazu bauen wir uns schnell ein paar Routinen. Die erste soll den Namen des Moduls entgegennehmen, deren Prozeduren mit Zeilennummerierungen versehen werden sollen (siehe Listing 2).

Public Sub AddLineNumbersToModule(strModule As String)
     Dim objVBProject As VBIDE.VBProject
     Dim objVBComponent As VBIDE.VBComponent
     Dim objCodemodule As VBIDE.CodeModule
     Dim intProcType As vbext_ProcKind
     Dim lngCountOfLines As Long
     Dim lngLine As Long
     Dim strProcedure As String
     Set objVBProject = VBE.ActiveVBProject
     Set objVBComponent = objVBProject.VBComponents(strModule)
     Set objCodemodule = objVBComponent.CodeModule
     lngCountOfLines = objCodemodule.CountOfLines
     For lngLine = 1 To lngCountOfLines
         If Not strProcedure = objCodemodule.ProcOfLine(lngLine, intProcType) Then
             strProcedure = objCodemodule.ProcOfLine(lngLine, intProcType)
             Call AddLineNumbers(strModule, strProcedure)
         End If
     Next lngLine
End Sub

Listing 2: Prozedur zum Hinzufügen von Zeilennummern zu allen Prozeduren eines Moduls

Sie referenziert über das VBProject-Objekt das VBComponents-Objekt für das Modul und verwendet dann das CodeModule dieses Objekts. Sie ermittelt die Anzahl der Zeilen des Moduls und durchläuft dann alle Zeilen des Moduls. Dabei vergleicht sie jeweils den Namen der Prozedur der aktuellen Zeile mit dem in strProcedure gespeicherten Namen.

Diese ist zunächst leer. Sobald die Routine auf die erste Prozedur stößt, wird der Name der Prozedur in strProcedure geschrieben und die Prozedur AddLineNumber wird für dieses Modul und diese Prozedur aufgerufen. Auf diese Weise durchläuft die Prozedur AddLineNumbersToModule alle Prozeduren des Moduls:

Zeilennummern zu allen Prozeduren des VBA-Projekts hinzufügen

Schließlich schauen wir uns noch an, wie wir allen Prozeduren in allen Modulen eines VBA-Projekts Zeilennummern hinzufügen können. Dazu schreiben wir eine weitere Prozedur, welche wiederum die Prozedur AddLineNumbersToModule aufruft:

Public Sub AddLineNumbersToActiveVBProject()
     Dim objVBProject As VBIDE.VBProject
     Dim objVBComponent As VBIDE.VBComponent
     Set objVBProject = VBE.ActiveVBProject
     For Each objVBComponent In objVBProject.VBComponents
         Call AddLineNumbersToModule(objVBComponent.Name)
     Next objVBComponent
End Sub

Die Prozedur referenziert das aktive VBA-Projekt und durchläuft alle Elemente der VBComponents-Auflistung, die sie dabei mit der Variablen objVBComponent referenziert. Sie ruft für jeden Schleifendurchlauf die Prozedur AddLineNumbersToModule mit dem Namen des aktuellen VBComponent-Objekts auf.

Funktion zum Entfernen von Zeilennummern aus einer Prozedur

Gegebenenfalls möchte man auch einmal die Zeilennummern wieder entfernen. Beispielsweise dann, wenn man Zeilen zum Code hinzufügen oder entfernen will und nicht die Nummerierung von Hand anpassen möchte.Dazu können wir die Prozedur RemoveLineNumbers aus Listing 3 nutzen.

Public Sub RemoveLineNumbers(strModule As String, strProcedure As String)
     Dim objVBProject As VBIDE.VBProject
     Dim objVBComponent As VBIDE.VBComponent
     Dim objCodemodule As VBIDE.CodeModule
     Dim lngCountOfLines As Long
     Dim intProcType As vbext_ProcKind
     Dim lngProcBodyLine As Long
     Dim lngFirstStatementLineNumber As Long
     Dim lngLastStatementLineNumber As Long
     Dim lngLine As Long
     Dim strLine As String
     Dim bolNumbers As Boolean
     Set objVBProject = VBE.ActiveVBProject
     Set objVBComponent = objVBProject.VBComponents(strModule)
     Set objCodemodule = objVBComponent.CodeModule
     lngCountOfLines = objCodemodule.CountOfLines
     lngProcBodyLine = objCodemodule.ProcBodyLine(strProcedure, intProcType)
     lngFirstStatementLineNumber = GetFirstStatementLineNumber(objCodemodule, lngProcBodyLine)
     lngLastStatementLineNumber = GetLastStatementLineNumber(objCodemodule, lngProcBodyLine)
     For lngLine = lngFirstStatementLineNumber To lngLastStatementLineNumber
         strLine = left(objCodemodule.Lines(lngLine, 1), 6)
         Select Case True
             Case IsNumeric(strLine)
                 bolNumbers = True
                 Exit For
         End Select
     Next lngLine
     If bolNumbers = True Then
         For lngLine = lngFirstStatementLineNumber To lngLastStatementLineNumber
             strLine = left(objCodemodule.Lines(lngLine, 1), 6)
             Select Case True
                 Case IsNumeric(strLine), Trim(strLine) = ""
                    objCodemodule.ReplaceLine lngLine, Mid(objCodemodule.Lines(lngLine, 1), 7)
             End Select
         Next lngLine
     End If
End Sub

Listing 3: Prozedur zum Entfernen von Zeilennummern einer Prozedur

Achtung: Die Prozedur geht davon aus, dass die Prozedur, aus der die Zeilennummern entfernt werden sollen, mit der weiter oben beschriebenen Prozedur AddLineNumbers mit Zeilennummern versehen wurde.

Die Prozedur erwartet die folgenden Parameter:

  • strModule: Name des Moduls, in dem sich die Prozedur befindet
  • strProcedure: Name der Prozedur, aus der die Zeilennummern entfernt werden sollen

Sie referenziert das aktuelle VBA-Projekt und dann mit objVBComponent die mit strModule übergebene Komponente. Mit objCodeModule referenzieren wir das CodeModule-Element, das wir anschließend nutzen, um mit ProcBodyLine die erste Zeile der Prozedur aus strProcedure zu ermitteln. Mit dieser können wir nun die beiden Funktionen GetFirstStatementLineNumber und GetLastStatementLineNumber aufrufen, um die Nummern der ersten und der letzten Zeile innerhalb der Prozedur zu ermitteln. Diese Werte verwenden wir als Bereich von zunächst einer For…Next-Schleife.

In dieser untersuchen wir die ersten sechs Zeichen der Zeile. Enthalten diese einen numerischen Wert, ist die Prozedur offensichtlich mit einer Zeilennummerierung ausgestattet. Sobald wir einen numerischen Wert in den ersten sechs Zeichen einer Zeile ermittelt haben, stellen wir daher die Variable bolNumbers auf True ein und verlassen die erste For…Next-Schleife. Hat bolNumbers danach den Wert True, steigen wir in die nächste For…Next-Schleife ein. In dieser trennen wir zunächst die ersten sechs Zeichen einer jeden Zeile ab und untersuchen diese anschließend. Ist der Inhalt der ersten sechs Zeichen numerisch, handelt es sich offensichtlich um Zeilennummern. Ist der Inhalt hingegen eine leere Zeichenkette, so ist es eine Zeile, die zuvor eingerückt wurde, ohne dass eine Zeilennummer hinzugefügt wurde. In beiden Fällen werden einfach die ersten sechs Zeichen von vorn entfernt.

Funktion zum Entfernen von Zeilennummern aus einem vollständigen Modul

Da wir diese Prozedur nicht für jede Prozedur einzeln aufrufen wollen, verwenden wir eine weitere Prozedur namens RemoveLineNumbersFromModule (siehe Listing 4), um alle Prozeduren eines Moduls zu durchlaufen und für jede Prozedur die Routine RemoveLineNumbers aufzurufen.

Public Sub RemoveLineNumbersFromModule(strModule As String)
     Dim objVBProject As VBIDE.VBProject
     Dim objVBComponent As VBIDE.VBComponent
     Dim objCodemodule As VBIDE.CodeModule
     Dim intProcType As vbext_ProcKind
     Dim lngCountOfLines As Long
     Dim lngLine As Long
     Dim strProcedure As String
     Set objVBProject = VBE.ActiveVBProject
     Set objVBComponent = objVBProject.VBComponents(strModule)
     Set objCodemodule = objVBComponent.CodeModule
     lngCountOfLines = objCodemodule.CountOfLines
     For lngLine = 1 To lngCountOfLines
         If Not strProcedure = objCodemodule.ProcOfLine(lngLine, intProcType) Then
             strProcedure = objCodemodule.ProcOfLine(lngLine, intProcType)
             Call RemoveLineNumbers(strModule, strProcedure)
         End If
     Next lngLine
End Sub

Listing 4: Prozedur zum Entfernen von Zeilennummern eines Moduls

Dazu referenzieren wir das aktuelle VBA-Projekt und das übergebene Modul mit der Variablen objCodeModule. Wir durchlaufen alle Zeilen des Moduls und prüfen, zu welcher Prozedur die aktuelle Zeile gehört. Beim Auffinden einer jeden neuen Prozedur rufen wir einmal die Prozedur RemoveLineNumbers für diese Prozedur auf. Dies führen wir solange durch, bis wir alle Zeilen des Moduls durchlaufen haben.

Funktion zum Entfernen von Zeilennummern aus dem vollständigen VBA-Projekt

Schließlich verwenden wir noch eine Prozedur namens RemoveLineNumbersFromActiveVBProject, um alle Module des aktuellen VBA-Projekts zu durchlaufen und für jedes Modul einmal die Prozedur RemoveLineNumbersFromModule aufzurufen.

Damit können wir alle Nummerierungen aus den Modulen des Projekts entfernen:

Public Sub RemoveLineNumbersFromActiveVBProject()
     Dim objVBProject As VBIDE.VBProject
     Dim objVBComponent As VBIDE.VBComponent
     Set objVBProject = VBE.ActiveVBProject
     For Each objVBComponent In objVBProject.VBComponents
         Call RemoveLineNumbersFromModule(objVBComponent.Name)
     Next objVBComponent
End Sub

Hilfsfunktion zum Ermitteln der Zeilennummer der ersten Anweisung einer Prozedur

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