Fehlendes Option Explicit finden und nachrüsten

Zwei Wörter am Anfang eines Moduls ersparen Ihnen Stunden der Fehlersuche: Option Explicit. Wer sie vergisst, bekommt von VBA jeden Tippfehler in einem Variablennamen klaglos als neue Variable durchgewinkt – und einen Fehler, der sich erst Wochen später als falsches Ergebnis zeigt. In gewachsenen Anwendungen fehlt die Anweisung erfahrungsgemäß in einem Teil der Module. Dieser Beitrag zeigt, wie Sie die Lücken per VBA aufspüren und schließen, ohne jedes Modul einzeln zu öffnen.

Warum die Anweisung unverzichtbar ist

Ohne Option Explicit darf jede Variable ohne Deklaration verwendet werden. VBA legt sie beim ersten Auftreten stillschweigend als Variant an. Was das bedeutet, zeigt folgendes Listing – ein einziger fehlender Buchstabe genügt:

Public Sub OhneOptionExplicit()
    Dim lngSumme As Long
    lngSumme = 100
    lngSume = lngSumme + 50    ' Tippfehler!
    Debug.Print lngSumme
End Sub

Die Ausgabe im Direktbereich lautet:

100

Erwartet hätte man 150. Stattdessen hat VBA die Variable lngSume neu angelegt, ihr das Ergebnis zugewiesen und sie danach nie wieder angesehen. Kein Fehler, keine Warnung – nur ein falscher Wert. Mit Option Explicit dagegen scheitert schon das Kompilieren mit der Meldung, dass die Variable nicht definiert ist.

Der Nebeneffekt ist ebenfalls bemerkenswert: Alle stillschweigend erzeugten Variablen sind vom Typ Variant und damit langsamer und speicherhungriger als nötig. Es gibt schlicht keinen guten Grund, ohne diese Anweisung zu arbeiten.

Die Einstellung im VBA-Editor

Damit die Anweisung in neuen Modulen automatisch erscheint, gibt es im VBA-Editor eine Option. Sie finden sie unter Extras/Optionen auf der Registerseite Editor unter dem Namen Variablendeklaration erforderlich (siehe Bild 1). Ist sie aktiviert, setzt Access die Anweisung in jedes neu angelegte Modul.

Die Option Variablendeklaration erforderlich im VBA-Editor

Bild 1: Die Option Variablendeklaration erforderlich im VBA-Editor

Und genau hier liegt das Problem, das diesen Beitrag nötig macht: Die Einstellung wirkt ausschließlich für Module, die danach neu entstehen. Bestehende Module bleiben unangetastet. Wer die Option erst nach Jahren entdeckt – oder eine Datenbank übernimmt, in der sie nie aktiv war – hat einen bunt gemischten Bestand vor sich. Die Einstellung gilt außerdem pro Installation und nicht pro Datenbank; auf dem Rechner eines Kollegen kann sie also anders stehen.

Vorbereitung: Verweis setzen

Um Module per Code zu untersuchen, greifen wir auf das Objektmodell des VBA-Editors zu. Dazu benötigen wir einen Verweis auf die Bibliothek Microsoft Visual Basic for Applications Extensibility 5.3 Object Library, den wir wie in Bild 2 setzen.

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

Bild 2: Setzen eines Verweises auf die Bibliothek Microsoft Visual Basic for Applications Extensibility 5.3 Object Library

Das eigene Projekt finden

Der VBA-Editor kann mehrere Projekte gleichzeitig geöffnet haben – etwa weitere Datenbanken oder Add-Ins. Wir brauchen deshalb eine Funktion, die gezielt das Projekt der aktuellen Datenbank heraussucht. Der Vergleich läuft über den Dateinamen (siehe Listing 1).

Public Function GetCurrentVBProject() As VBIDE.VBProject
    Dim objVBProject As VBIDE.VBProject
    For Each objVBProject In Application.VBE.VBProjects
        If StrComp(objVBProject.FileName, CurrentProject.FullName, vbTextCompare) = 0 Then
            Set GetCurrentVBProject = objVBProject
            Exit Function
        End If
    Next objVBProject
End Function

Listing 1: Ermitteln des VBA-Projekts der aktuellen Datenbank

Ein Test im Direktbereich zeigt, ob die Vorbereitungen geglückt sind:

Debug.Print GetCurrentVBProject.Name
Debug.Print GetCurrentVBProject.VBComponents.Count
Beispieldatenbank
14

Die Zahl umfasst dabei alle Standard- und Klassenmodule sowie die Klassenmodule von Formularen und Berichten. Letztere erscheinen unter Namen wie Form_frmArtikel – allerdings nur, wenn das Formular überhaupt ein Modul besitzt, seine Eigenschaft Enthält Modul also auf Ja steht.

Ein Hinweis zur Fehlerbehandlung: Die Eigenschaft FileName löst einen Fehler aus, wenn ein Projekt noch nie gespeichert wurde. Kommen bei Ihnen mehrere Projekte ins Spiel, sollten Sie die Schleife mit On Error Resume Next absichern.

Prüfen, ob ein Modul „Option Explicit“ enthält

Nun zur eigentlichen Prüfung, ob ein Modul die Anweisung Option Explicit enthält. Diese führen wir mit der Funktion HasOptionExplicit durch (siehe Listing 2). Option Explicit steht immer im Deklarationsbereich, also oberhalb der ersten Prozedur. Wie viele Zeilen dieser umfasst, verrät die Eigenschaft CountOfDeclarationLines – wir müssen also nicht das ganze Modul durchsuchen.

Public Function HasOptionExplicit(ByVal objCodeModule As VBIDE.CodeModule) As Boolean
    Dim lngLineNumber As Long
    Dim lngCommentPosition As Long
    Dim strLine As String
    For lngLineNumber = 1 To objCodeModule.CountOfDeclarationLines
        strLine = Trim$(objCodeModule.Lines(lngLineNumber, 1))
        ' Kommentar am Ende der Zeile entfernen
        lngCommentPosition = InStr(strLine, "'")
        If lngCommentPosition > 0 Then
            strLine = Trim$(Left$(strLine, lngCommentPosition - 1))
        End If
        If StrComp(strLine, "Option Explicit", vbTextCompare) = 0 Then
            HasOptionExplicit = True
            Exit Function
        End If
    Next lngLineNumber
End Function

Listing 2: Prüfen eines einzelnen Moduls

Zwei Details sind interessant. Die Methode Lines erwartet Startzeile und Anzahl, liefert hier also genau eine Zeile. Und der Vergleich mit StrComp und vbTextCompare ignoriert die Groß- und Kleinschreibung – ein option explicit in Kleinbuchstaben wird also ebenfalls erkannt. Das Abschneiden am Apostroph sorgt dafür, dass auch eine Zeile mit angehängtem Kommentar richtig bewertet wird.

So sieht der Aufruf für ein bestimmtes Modul aus:

Dim objVBProject As VBIDE.VBProject
Set objVBProject = GetCurrentVBProject()
Debug.Print HasOptionExplicit(objVBProject. _
    VBComponents("mdlGarKeinOption").CodeModule)

Die Anweisung „Option Explicit“ einfügen

Fehlt die Anweisung in einem Modul, tragen wir sie nach. Diese Aufgabe übernimmt die Prozedur InsertOptionExplicit aus Listing 3. Sie erwartet als einzigen Parameter das CodeModule-Objekt des Moduls, in dem Option Explicit ergänzt werden soll.

Public Sub InsertOptionExplicit(ByVal objCodeModule As VBIDE.CodeModule)
    Dim lngLineNumber As Long
    Dim lngOptionCompareLine As Long
    Dim strLine As String
    For lngLineNumber = 1 To objCodeModule.CountOfDeclarationLines
        strLine = Trim$(objCodeModule.Lines(lngLineNumber, 1))
        If StrComp(Left$(strLine, 14), "Option Compare", vbTextCompare) = 0 Then
            lngOptionCompareLine = lngLineNumber
            Exit For
        End If
    Next lngLineNumber
    If lngOptionCompareLine = 0 Then
        objCodeModule.InsertLines 1, "Option Explicit"
    Else
        objCodeModule.InsertLines lngOptionCompareLine, "Option Explicit"
    End If
End Sub

Listing 3: Die Prozedur InsertOptionExplicit ergänzt die fehlende Anweisung

Die Prozedur arbeitet in zwei Schritten. Im ersten durchläuft die For-Schleife die Zeilen des Deklarationsbereichs, deren Anzahl die Eigenschaft CountOfDeclarationLines liefert.

Die Methode Lines holt die jeweilige Zeile in die Variable strLine, wobei Trim$ führende Leerzeichen entfernt. Beginnt die Zeile mit Option Compare, merkt sich die Prozedur die Zeilennummer in lngOptionCompareLine und verlässt die Schleife mit Exit For.

Im zweiten Schritt entscheidet der Wert dieser Variablen, wo die neue Zeile landet. Steht dort noch die 0, gibt es im Modul keine Option Compare-Anweisung, und InsertLines setzt Option Explicit in Zeile 1. Andernfalls übergeben wir InsertLines die gemerkte Zeilennummer.

Dabei ist zu beachten, dass InsertLines die neue Zeile vor der angegebenen Zeilennummer einfügt. Option Explicit steht anschließend also über Option Compare Database. Für die Funktion ist das gleichgültig, denn die Reihenfolge der Option-Anweisungen spielt keine Rolle. Wer die gewohnte Schreibweise bevorzugt, übergibt stattdessen lngOptionCompareLine + 1.

Für einen Test rufen wir die Prozedur mit dem CodeModule eines einzelnen Moduls auf und lassen uns danach die ersten Zeilen des Deklarationsbereichs ausgeben:

Dim objCodeModule As VBIDE.CodeModule
Set objCodeModule = GetCurrentVBProject(). _
    VBComponents("mdlImport").CodeModule
InsertOptionExplicit objCodeModule
Debug.Print objCodeModule.Lines(1, 2)

Alle Module in einem Durchgang nachrüsten

Damit sind alle Bausteine beisammen. Die Prozedur AddOptionExplicitToAllModules aus Listing 4 setzt sie zusammen und erledigt die Arbeit für die komplette Anwendung.

Public Sub AddOptionExplicitToAllModules()
    Dim objCurrentVBProject As VBIDE.VBProject
    Dim objVBComponent As VBIDE.VBComponent
    Dim objCodeModule As VBIDE.CodeModule
    Set objCurrentVBProject = GetCurrentVBProject()
    If objCurrentVBProject Is Nothing Then
        MsgBox "Das aktuelle VBA-Projekt wurde nicht gefunden.", vbExclamation
        Exit Sub
    End If
    If objCurrentVBProject.Protection <> vbext_pp_none Then
        MsgBox "Das aktuelle VBA-Projekt ist geschützt.", vbExclamation
        Exit Sub
    End If
    For Each objVBComponent In objCurrentVBProject.VBComponents
        Set objCodeModule = objVBComponent.CodeModule
        If Not HasOptionExplicit(objCodeModule) Then
            InsertOptionExplicit objCodeModule
        End If
    Next objVBComponent
End Sub

Listing 4: AddOptionExplicitToAllModules rüstet alle Module der Anwendung nach

Access im Unternehmen

Unser exklusives Angebot für Dich!

Access im Unternehmen
13,25 € im Monat*

(Gilt für den Abschluss eines Jahres-Abonnements im ersten Jahr, danach 189,-/Jahr)

Hier geht’s weiter →

Die ersten 4 Wochen kostenlos testen – voller Zugriff auf alle Artikel, vollständigen Code und Beispieldatenbanken. Kein Risiko: Wenn es nicht passt, kündigst Du einfach innerhalb der ersten vier Wochen.

PayPal VISA Mastercard SEPA
Kostenlos & unverbindlich

Hast Du eine konkrete Frage zu Deiner eigenen Access-Anwendung?

Vielleicht stellt Deine Anwendung Dich vor eine Herausforderung, zu der Du bisher keine Lösung findest. Schlechte Performance, kein ausreichender Zugriffsschutz, Du bist unsicher über Dein Datenmodell oder Dein Code liefert unerklärliche Fehler?

In unserem kostenlosen Access-Audit schaut sich André Minhorst persönlich gemeinsam mit Dir Deine Lösung per Zoom an – und zeigt Dir, wo Datenmodell, VBA-Code, Ergonomie und Sicherheit Optimierungspotenzial bieten.

Jetzt kostenloses Access-Audit anfordern →