Verwaiste API-Funktionen finden

Wenn Sie eine größere Anwendung pflegen müssen, kann es hin und wieder sinnvoll sein, nicht mehr verwendete Routinen rauszuwerfen oder zumindest auszukommentieren. Das ist gerade praktisch, wenn Sie die API-Funktionen von 32-Bit auf 64-Bit umstellen wollen: Um hier Arbeit zu sparen, können Sie erst einmal alle API-Funktionen auskommentieren, die nicht mehr benötigt werden. Um die verwaisten Routinen zu finden, gibt es verschiedene Möglichkeiten: Sie können dies durch Auskommentieren und Testen manuell erledigen, ein Tool wie MZ-Tools einsetzen oder auch ein selbst programmiertes Tool. Letzteres könnte der entsprechenden Funktion von MZ-Tools noch einen draufsetzen und auch die Aufrufe aus den Eigenschaften von Formularsteuerelementen oder Abfragen ermitteln.

Hintergrund: 32-Bit- versus 64-Bit-Access

Mit Office 2019 hat Microsoft erstmalig die 64-Bit-Variante des Office-Pakets als Standard bei der Installation festgelegt. Zuvor war das noch die 32-Bit-Version. Wer also nicht gezielt die 64-Bit-Version installieren wollte, erhielt dort noch die 32-Bit-Version.

Das führte dazu, dass hier und da das Problem auftauchte, dass die für 32-Bit-Anwendungen ausgelegten API-Funktionen unter VBA nicht mehr funktionierten. Sie mussten an einigen Stellen angepasst werden, damit sie mit der 64-Bit-Version kompatibel waren.

Zu dieser Zeit konnte man Kunden in einigen Fällen noch zur Neuinstallation von Access in der 32-Bit-Version bewegen – auch vor dem Hintergrund, dass auch wichtige Steuerelemente wie das TreeView-Steuerelement und andere Elemente der Bibliothek MSCOMCTL.ocx nur unter 32-Bit zur Verfügung standen. Nunmehr ist auch das kein Grund mehr, nicht die 64-Bit-Version zu verwenden, denn die MSCOMCTL.ocx-Bibliothek steht nun auch dort zur Verfügung.

Also steht bei vielen Entwicklern nun auch die Migration von 32-Bit zu 64-Bit auf dem Programm. In den beiden Beiträgen API-Funktionen finden und speichern (www.access-im-unternehmen.de/1312) und API-Typen und -Konstanten finden und speichern (www.access-im-unternehmen.de/1313) haben wir bereits einige Vorbereitungen getroffen und Routinen programmiert, die alle API-Funktionen eines VBA-Projekts auslesen und in Tabellen schreiben – das Gleiche für die Konstanten und Deklarationen eines VBA-Projekts.

Nun benötigen wir noch ein Werkzeug, mit dem wir entscheiden können, welche dieser Elemente überhaupt in der Anwendung benötigt werden – und welche wir gegebenenfalls einfach auskommentieren können und nicht mehr anzupassen brauchen. Genau das erledigen wir in diesem Beitrag.

Woher kommen überzählige API-Funktionen

Eine Frage, die sich hier stellt, ist folgende: Woher kommen denn eigentlich überzählige API-Funktionen Sollte man nicht annehmen, dass der Programmierer nur die unbedingt benötigten API-Funktionen zu seinem VBA-Projekt hinzufügt Und das überzählige API-Funktionen nur dann anfallen, wenn man sich im jeweiligen Fall für eine alternative Technik entscheidet oder die Anforderung einfach wegfällt und man dann vergisst, die API-Funktion zu entfernen

In der Tat gibt es meist komplette Module mit den Deklarationen von Typen, Konstanten und Variablen samt API-Funktionen, die einen zusammenhängenden Bereich abdecken – beispielsweise die Verschlüsselung oder Grafikfunktionen. Wenn man eine benötigte API-Funktion in einem solchen Modul vorfindet, fügt man es in der Regel auch komplett zum VBA-Projekt hinzu – einfach, weil es zu aufwendig erscheint, nur die benötigten Elemente zu verwenden. Wenn Sie jedoch ein komplettes Modul mit GDI-Funktionen zum Projekt hinzufügen und nur eine Funktion daraus nutzen, sollten Sie spätestens bei einer drohenden Migration von 32-Bit nach 64-Bit überlegen, den Umfang auf die benötigten Elemente zu reduzieren.

Basis: Funktionsname

Wir wollen in diesem Beitrag davon ausgehen, dass eine mehr oder weniger große Menge von API-Funktionen vorliegt, die wir entweder aus der Tabelle entnehmen, die wir im Beitrag API-Funktionen finden und speichern erstellt und gefüllt haben oder die wir einfach so als Parameter an die Funktion zum Untersuchen von Aufrufen dieser Funktion übergeben.

Damit ist auch schon das Grundgerüst definiert: Wir benötigen eine VBA-Funktion, der wir den Namen einer API-Funktion übergeben und die das komplette VBA-Projekt nach Aufrufen dieser Funktion durchsucht.

Wie aber finden wir solche Aufrufe Dazu suchen wir nach Zeilen, die folgende Bedingungen erfüllen:

  • Die Zeile enthält den Namen der API-Funktion, gefolgt von einer öffnenden Klammer – also zum Beispiel Sleep( -, gefolgt von einem Leerzeichen oder gefolgt von einem Zeilenumbruch.
  • Die Zeile enthält nicht die Deklaration der API-Funktion selbst. Diese ist leicht zu definieren, nämlich durch das Schlüsselwort Declare.
  • Die Zeile ist keine Kommentarzeile.

Ein API-Aufruf kann nämlich beispielsweise wie folgt aussehen:

Sleep 30 ''mit Leerzeichen
lngDriveType = GetDriveType("c:") ''mit öffnender Klammer
EmptyClipboard ''mit folgendem Zeilenumbruch

Speichern der Ergebnisse in einer Tabelle

Im Beitrag API-Funktionen finden und speichern haben wir bereits eine Tabelle namens tblAPIDeclarations vorgestellt, in die wir mir einer ebenfalls in diesem Beitrag vorgestellten Routine alle Deklarationen von API-Funktionen eintragen.

Diese Tabellen wollen wir für den vorliegenden Beitrag um ein Feld erweitern. Dieses soll den Namen IsCalled erhalten.

Sobald wir den Aufruf einer der in der Tabelle gespeicherten API-Funktionen gefunden haben, stellen wir den Wert dieses Feldes auf den Wert True ein (siehe Bild 1).

Tabelle zum Speichern der API-Deklarationen eines VBA-Projekts

Bild 1: Tabelle zum Speichern der API-Deklarationen eines VBA-Projekts

Prozedur zum Auffinden von API-Aufrufen

Die Prozedur FindAPICalls erwartet lediglich die Übergabe des VB-Projekts als Parameter. Sie referenziert mit der Objektvariablen db das aktuelle Database-Objekt und nutzt dessen Execute-Methode, um mit einer UPDATE-Abfrage den Wert des Feldes IsCalled auf den Wert False einzustellen (siehe Listing 1).

Public Sub FindAPICalls(objVBproject As VBProject)
     Dim objVBComponent As VBComponent, objCodeModule As CodeModule
     Dim db As DAO.Database, rst As DAO.Recordset
     Dim lngStartLine As Long, lngEndLine As Long, lngStartColumn As Long, lngEndColumn As Long
     Dim strLine As String
     Dim bolApiCall As Boolean, bolFound As Boolean
     Set db = CurrentDb
     db.Execute "UPDATE tblAPIDeclarations SET IsCalled = False", dbFailOnError
     Set rst = db.OpenRecordset("SELECT * FROM tblAPIDeclarations", dbOpenDynaset)
     Do While Not rst.EOF
         Debug.Print rst!ApiCall
         For Each objVBComponent In objVBproject.VBComponents
             Set objCodeModule = objVBComponent.CodeModule
             lngStartLine = 0
             lngStartColumn = 0
             lngEndLine = 0
             lngEndColumn = 0
             bolFound = objCodeModule.Find(rst!ApiCall & "(", lngStartLine, lngStartColumn, lngEndLine, lngEndColumn) _
                 Or objCodeModule.Find(rst!ApiCall & " ", lngStartLine, lngStartColumn, lngEndLine, lngEndColumn) _
                 Or objCodeModule.Find(rst!ApiCall & vbCrLf, lngStartLine, lngStartColumn, lngEndLine, lngEndColumn)
             Do While bolFound = True
                 bolApiCall = True
                 strLine = objCodeModule.Lines(lngStartLine, 1)
                 strLine = Trim(strLine)
                 If Not InStr(1, strLine, "''") = 0 Then
                     If InStr(1, strLine, "''") < InStr(1, strLine, rst!ApiCall) Then
                         bolApiCall = False
                     End If
                 End If
                 If Not InStr(1, strLine, "Declare ") = 0 Then
                     bolApiCall = False
                 End If
                 If bolApiCall = True Then
                     rst.Edit
                     rst!IsCalled = True
                     rst.Update
                     Exit For
                 End If
                 lngStartLine = lngStartLine + 1
                 lngEndLine = 0
                 bolFound = objCodeModule.Find(rst!ApiCall & "(", lngStartLine, lngStartColumn, lngEndLine, lngEndColumn) _
                      Or objCodeModule.Find(rst!ApiCall & " ", lngStartLine, lngStartColumn, lngEndLine, lngEndColumn) _
                     Or objCodeModule.Find(rst!ApiCall & vbCrLf, lngStartLine, lngStartColumn, lngEndLine, lngEndColumn)
             Loop
         Next objVBComponent
         rst.MoveNext
     Loop
End Sub

Sie haben das Ende des frei verfügbaren Textes erreicht. Möchten Sie ...

TestzugangOder haben Sie bereits Zugangsdaten? Dann loggen Sie sich gleich hier ein:

Schreibe einen Kommentar