API-Typen und -Konstanten finden und speichern

Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.

Im Beitrag “API-Funktionen finden und speichern” haben wir gezeigt, wie Sie alle API-Deklarationen eines VBA-Projekts finden und sowohl die Daten der API-Funktion als auch die der Parameter in zwei Tabellen speichern. Zu API-Deklarationen gehören jedoch auch einige Konstanten und Typen, die beim Aufruf der API-Funktionen verwendet werden oder als Parameter der Funktionen dienen. Im Sinne des Schaffens einer Möglichkeit zum Migrieren von API-Deklarationen von 32-Bit zu 64-Bit wollen wir auch diese Elemente zunächst in entsprechenden Tabellen speichern, um diese dann per Code anzupassen und in der 64-Bit-Version auszugeben.

Vorarbeiten

Im Beitrag API-Funktionen finden und speichern (www.access-im-unternehmen.de/1312) haben wir bereits eine Prozedur angelegt, die alle Module einer Datenbank durchläuft und die enthaltenen API-Deklarationen ausliest. Dieser fügen wir einen weiteren Aufruf hinzu, und zwar für eine Prozedur namens FindAPITypes:

Public Sub FindAPIDeclares()
     Dim objVBProject As VBProject
     Dim objVBComponent As VBComponent
     Set objVBProject = GetCurrentVBProject
     For Each objVBComponent In objVBProject.VBComponents
         FindAPIDeclaresInModule objVBComponent
         FindAPITypes objVBComponent
     Next objVBComponent
End Sub

Die Prozedur FindAPITypes und die von dieser Prozedur aufgerufenen Routinen sehen wir uns im vorliegenden Beitrag an.

Wie sind Type-Konstrukte aufgebaut

Bevor wir Code programmieren können, mit denen wir Type-Elemente einlesen, müssen wir uns erst einmal ansehen, wie diese Elemente aufgebaut sind. Ein Type-Element besteht immer mindetens aus dem Type-Schlüsselwort und dem Namen des Types in der ersten Zeile, einem Element, das aus dem Elementnamen, dem Schlüsselwort As und dem Variablentyp besteht, und dem Abschluss mit der Zeile End Type:

Type bla
     blub As String
End Type

Das Type-Element kann in der ersten Zeile auch noch ein Schlüsselwort enthalten, das die Gültigkeit bezeichnet, also Private oder Public.

Eines der im Type-Konstrukt enthaltenen Elemente mit dem Datentyp String kann auch noch die Angabe einer festen Zeichenanzahl enthalten, zum Beispiel mit der Zeichenanzahl 50:

blub As String * 50

Ein praktisches Beispiel ist das folgende:

Private Type shellBrowseInfo
     hwndOwner      As Long
     pIDLRoot       As Long
     pszDisplayName As Long
     lpszTitle      As String
     ulFlags        As Long
     lpfnCallback   As Long
     lParam         As Long
     iImage         As Long
End Type

Typen in VBA-Modulen finden

In der oben vorgestellten Prozedur namens FindAPIDeclares durchlaufen wir alle VBComponent-Objekte, also alle Module des aktuellen VBA-Projekts. Für jedes Modul rufen wir einmal die Prozedur FindAPITypes auf und übergeben dieser einen Verweis auf das jeweilige Modul:

FindAPITypes objVBComponent

Die Prozedur FindAPITypes finden Sie in Listing 1. Die Prozedur nimmt das VBComponent-Objekt mit dem Parameter objVBComponent entgegen und füllt die Variable objCodeModule mit dem Objekt aus der Eigenschaft CodeModule der VBA-Komponente aus objVBComponent.

Public Sub FindAPITypes(objVBComponent As VBComponent)
     Dim objCodeModule As CodeModule
     Dim lngLine As Long
     Dim strLine As String
     Dim strType As String
     Set objCodeModule = objVBComponent.CodeModule
     For lngLine = 1 To objCodeModule.CountOfDeclarationLines
         strLine = Trim(objCodeModule.Lines(lngLine, 1))
         strLine = ReplaceMultipleSpaces(strLine)
         If Left(strLine, 5) = "Type " Or Left(strLine, 13) = "Private Type " Or Left(strLine, 12) = "Public Type " Then
             strType = strLine & vbCrLf
             Do While Not Left(strLine, 8) = "End Type"
                 lngLine = lngLine + 1
                 strLine = Trim(objCodeModule.Lines(lngLine, 1))
                 strLine = ReplaceMultipleSpaces(strLine)
                 strType = strType & strLine & vbCrLf
             Loop
             SaveAPIType strType, objVBComponent.Name
         End If
     Next lngLine
End Sub

Listing 1: Prozedur zum Finden der API-Type-Strukturen

Dann durchlaufen wir alle Zeilen des Deklarationsbereichs des jeweils aktuellen Moduls aus objCodeModule in einer For Next-Schleife von der Zeile 1 bis zu der mit CountOfDeclaration ermittelten letzten Zeile des Deklarationsbereichs. Hier übertragen wir die aktuelle Zeile, von der wir mit der Trim-Funktion überschüssige Leerzeichen vorn und hinten entfernen, in die Variable strLine. Diese schicken wir dann durch die Funktion ReplaceMultipleSpaces. Damit wollen wir überschüssige Leerzeichen aus der aktuellen Zeile entfernen, sodass aus der Zeile

Private Type       Beispiel

die folgende Zeile wird:

Private Type Beispiel

Die dazu verwendete Funktion ReplaceMultipleSpaces sieht wie folgt aus und durchläuft solange eine Do While-Schleife, bis keine doppelten Leerzeichen mehr in der mit dem Parameter strLine übergebenen Zeichenkette enthalten sind. Innerhalb der Do While-Schleife ersetzt die Funktion jeweils ein doppeltes Leerzeichen durch ein einfaches Leerzeichen. Anschließend gibt sie die Zeichenkette aus strLine als Rückgabewert der Funktion zurück:

Public Function ReplaceMultipleSpaces( ByVal strLine As String) As String
     Do While Not InStr(1, strLine, "  ") = 0
         strLine = VBA.Replace(strLine, "  ", " ")
     Loop
     ReplaceMultipleSpaces = strLine
End Function

Die so behandelte Zeile landet in der aufrufenden Prozedur wieder in der Variablen strLine. Nachdem diese nun keine führenden oder folgenden Leerzeichen mehr aufweist, können wir die aktuelle Zeile dahingehend überprüfen, ob diese die erste Zeile eines Type-Konstrukts ist.

Dafür testen wir, ob die ersten fünf Zeichen der Zeichenkette “Type “, die ersten dreizehn Zeichen der Zeichenkette “Private Type ” oder die ersten zwölf Zeichen der Zeichenkette “Public Sub ” lauten. Ist das der Fall, fügen wir der Variablen strType die erste Zeile des Type-Konstrukts inklusive eines Zeilenumbruchs hinzu.

Danach durchlaufen wir solange eine Do While-Schleife, bis wir auf eine Zeile stoßen, die mit der Zeichenfolge End Type beginnt – also bis zur letzten Zeile des Type-Konstrukts. Dabei erhöhen wir die Zählervariable lngLine jeweils um 1 und lesen die jeweilige Zeile aus objCodeModule.Lines(lngLine, 1) in die Variable strLine ein.

In dieser Zeile entfernen wir wieder mit der Funktion ReplaceMultipleSpaces überflüssige Leerzeichen. Den Grund dafür erfahren Sie übrigens weiter unten. Danach fügen wir die bereinigte aktuelle Zeile als neue Zeile an die Zeichenkette strType an und hängen noch ein vbCrLf hinten an.

Dies alles geschieht, wie oben bereits beschrieben, bis wir in strLine den Ausdruck End Type finden. Damit ist das Type-Konstrukt komplett in die Variable strType eingelesen. Dieses übergeben wir dann nebst dem Namen der VBA-Komponente an eine weitere Prozedur namens SaveAPIType.

Tabelle zum Speichern der Type-Elemente anlegen

Um die Type-Konstrukte beziehungsweise deren Informationen zu speichern, verwenden wir zwei Tabellen. Die Erste heißt tblAPITypes und sieht in der Entwurfsansicht wie in Bild 1 aus. Die Felder speichern die folgenden Informationen:

Entwurf der Tabelle tblAPITypes

Bild 1: Entwurf der Tabelle tblAPITypes

  • APITypeID: Primärschlüsselfeld der Tabelle
  • APITypeName: Name des Type-Konstrukts, also der in der ersten Zeile angegebene Bezeichner
  • Module: Modul, in dem sich die Definition des Type-Elements befindet
  • Visibility: Sichtbarkeit, als entweder Private oder Public
  • APIType: Text des kompletten Typs inklusive der darin definierten Elemente

Speichern des Type-Konstrukts

Das Speichern erledigt die Prozedur SaveAPIType. Sie erwartet mit dem ersten Parameter den kompletten Text des Type-Konstrukts und mit dem zweiten den Namen des Moduls, in dem sich der Type befindet (siehe Listing 2).

Public Sub SaveAPIType(ByVal strAPIType As String, ByVal strModule As String)
     Dim strLines() As String
     Dim strLine As String
     Dim db As dao.Database
     Dim rstTypes As dao.Recordset
     Dim strVisibility As String
     Dim strTypeElements() As String
     Dim lngTypeID As Long
     Dim strAPITypeName As String
     Set db = CurrentDb
     Set rstTypes = db.OpenRecordset("SELECT * FROM tblAPITypes", dbOpenDynaset)
     If Right(strAPIType, 2) = vbCrLf Then
         strAPIType = Left(strAPIType, Len(strAPIType) - 2)
     End If
     strLines = Split(strAPIType, vbCrLf)
     strLine = strLines(0)
     strTypeElements = Split(strLine, " ")
     Select Case UBound(strTypeElements) - LBound(strTypeElements) + 1
         Case 2
             strVisibility = "Public"
             strAPITypeName = Split(strLine, " ")(1)
         Case 3
             strVisibility = Split(strLine, " ")(0)
             strAPITypeName = Split(strLine, " ")(2)
     End Select
     With rstTypes
         .AddNew
         !APITypeName = strAPITypeName
         !Module = strModule
         !Visibility = strVisibility
         !APIType = strAPIType
         lngTypeID = !APITypeID
         .Update
     End With
     SaveAPITypeElements db, strLines, lngTypeID
End Sub

Listing 2: Prozedur zum Speichern der API-Typen in der Tabelle tblAPITypes

Gleich zu Beginn referenziert die Prozedur mit der Variablen db das Database-Objekt der aktuellen Datenbank. Damit rufen wir die Methode OpenRecordset auf, um eine Abfrage auf Basis der Tabelle tblAPITypes als Recordset zu öffnen und mit rstTypes zu referenzieren.

Sollte das mit strAPIType übergebene Type-Konstrukt noch einen Zeilenumbruch am Ende enthalten, wird dieser zunächst entfernt. Danach füllt die Prozedur das Array strLines mit den Zeilen aus strAPIType. Dazu verwendet es die Split-Funktion und spaltet den Inhalt aus strAPIType jeweils an den Zeilenumbrüchen in einzelne Array-Elemente auf.

Die erste Zeile erfassen wir nun mit strLines(0) und schreiben diese in die Variable strLine. Nun folgt der Grund, warum wir weiter oben so darauf bedacht waren, doppelte Leerzeichen sowie führende und folgende Leerzeichen aus den Zeilen zu verbannen: Wir füllen ein weiteres Array mit den einzelnen Elementen der ersten Zeile. Diese sieht vorher beispielsweise wie folgt aus:

Public Type Beispiel

Die drei Elemente landen nun in einem Array namens strTypeElements, sodass dieses die folgenden Elemente enthält:

strTypeElements(0): Public
strTypeElements(1): Type
strTypeElements(2): Beispiel

Es könnte auch sein, dass die erste Zeile so lautet:

Type Beispiel

Dann sieht das Array so aus:

strTypeElements(0): Type
strTypeElements(1): Beispiel

Die Elemente, die wir gleich in die Tabelle tblAPITypes schreiben wollen, befinden sich nun also je nach Type-Definition an verschiedenen Positionen im Array.

Deshalb prüfen wir in der folgenden Select Case-Bedingung die Anzahl der Elemente des Arrays strTypeElements, indem wir vom Wert des größten Index (zum Beispiel 2) den Wert des kleinsten Index (zum Beispiel 0) subtrahieren und 1 addieren, was für Public Type Beispiel die Anzahl 3 ergibt.

Im Falle von 2 gehen wir davon aus, dass nur das Schlüsselwort Type und der Name vorhanden sind, sodass der Benutzer nicht explizit angegebenen hat, ob der Type privat oder öffentlich sein soll.

In diesem Fall wird implizit ein öffentlicher Type verwendet. Also schreiben wir in die Variable Visibility den Wert Public. In die Variable strAPITypeName tragen wir dann das zweite Element des Arrays ein, beispielsweise den Namen Beispiel.

Wenn die Select Case-Bedingung den Zweig Case 3 ansteuert, wir es also mit einer ersten Zeile wie Public Type Beispiel zu tun haben, verwenden wir den ersten Wert des Arrays für die Variable strVisibility und den dritten als strAPITypeName.

Damit ausgestattet fügen wir schließlich einen neuen Datensatz zum Recordset rstTypes für die Tabelle tblAPITypes hinzu und legen die Werte der Felder APITypeName (aus strAPITypeName), Module (aus dem Parameter strModule), Visibility (aus strVisibility) und APIType (aus dem Parameter strAPIType) fest. Bevor wir den Datensatz mit der Update-Methode speichern, lesen wir noch den Primärschlüsselwert des neuen Datensatzes in die Variable lngTypeID ein.

Diesen übergeben wir dann samt der Variablen db und den in strLines gespeicherten einzelnen Zeilen des Types an die Prozedur SaveAPITypeElements weiter, welche die Informationen über die einzelnen Typen in der Tabelle tblAPITypesElements speichern soll.

Tabelle zum Speichern der Elemente des Type-Konstrukts

Die Tabelle zum Speichern der Informationen der einzelnen Elemente des Type-Konstrukts heißt tblAPITypesElements und sieht in der Entwurfsansicht wie in Bild 2 aus.

Entwurf der Tabelle tblAPITypesElements

Bild 2: Entwurf der Tabelle tblAPITypesElements

Sie enthält die folgenden Felder:

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

Testzugang

eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar