API-Prozeduren einbinden

API-Prozeduren sind in speziellen Dateien enthalten, die sich zumeist im Windowsverzeichnis befinden. Meistens handelt es sich dabei um Dateien vom Typ DLL. So gibt beispielsweise die Funktion GetSystemDirectory aus der Datei kernel.dll zurück, wo sich das Windows-Systemverzeichnis befindet. Um diese Funktion in VBA zu nutzen, muss im Deklarationsbereich eines Moduls mit einer Declare-Anweisung auf sie verwiesen werden:

Private Declare PtrSafe Function GetSystemDirectory Lib "kernel32" _
    Alias "GetSystemDirectoryA" _
    (ByVal lpBuffer As String, nSize As Long) As Long

Die Syntax von Declare setzt sich wie folgt zusammen:

[Public|Private] Declare [PtrSafe] Sub|Function Name Lib "Datei" [Alias "Aliasname"] ([Argumente]) [As Typ]

Eigentlich ist damit auch schon alles erledigt: Uns steht nun eine neue Funktion namens GetSystemDirectory zur Verfügung (in dieser Syntax zumindest ab Office 2010, aber darauf gehen wir noch ein). Der Teufel liegt in zahlreichen Details. Unter anderem, weil APIs in Programmiersprachen geschrieben sind, die teils andere Datentypen haben. Aber auch die Art und Weise, wie Ergebnisse zurückgegeben werden, ist im ersten Moment ungewohnt.

Bei GetSystemDirectory interessiert uns beispielweise nicht etwa der Rückgabewert vom Typ Long, wie man das im ersten Moment beim Lesen der Declare-Anweisung erwarten könnte, sondern der Parameter lpBuffer, den man beim Aufruf der Funktion schon „leer“ übergeben muss. Die Funktion überschreibt diesen String dann mit dem eigentlichen Rückgabewert. Mit „leerem“ String ist dabei ein String bestimmter Länge, bestehend aus Null-Byte-Zeichen (vbNullChar) gemeint. Das ist notwendig, weil die Verwaltung des Arbeitsspeichers für Strings in der DLL anders abläuft als in VBA, und deswegen ist es auch wichtig, ob die Parameter ByRef oder ByVal übergeben werden. Diese Art, an das Ergebnis einer API-Prozedur zu kommen, ist typisch. Der eigentliche Rückgabewert enthält dagegen, je nach Prozedur, ganz unterschiedliche Informationen: Manchmal bedeutet „0“ z. B., dass die Funktion erfolgreich abgearbeitet wurde, und Werte ungleich „0“ sind Fehlernummern. Bei anderen Prozeduren gibt er an, wie viele Zeichen der gesuchte Text enthält, oder wie lang der „leere“ String sein müsste, um den zurückzugebenden String vollständig unterzubringen. Denn bei Strings ist auch das typisch bei APIs: Oft wird auch die Länge des Strings an die API übergeben, und hinterher muss man nicht benötigte vbNullChar-Zeichen am Ende abschneiden.

Aufgrund all dieser Besonderheiten ist es oft sinnvoll, zu einer Declare-Anweisung noch eine Prozedur zu schreiben, über die der eigentliche Aufruf erfolgt:

Private Declare PtrSafe Function GetSystemDirectoryA Lib "kernel32" _
    (ByVal lpBuffer As String, ByVal nSize As Long) As Long

Public Function GetSystemDirectory() As String Dim lngLen As Long, lpBuffer As String, nSize As Long nSize = 255 lpBuffer = String(nSize, vbNullChar) lngLen = GetSystemDirectoryA(lpBuffer, nSize) GetSystemDirectory = Left(lpBuffer, lngLen) End Function

Nun ist der eigentliche Aufruf ganz einfach:

Debug.Print GetSystemDirectory()
C:\Windows\system32