Windows-Verzeichnis mit API

Das Windows-Verzeichnis mithilfe des Win32-API ermitteln

Das Win32-API ist nicht VBA! Man könnte es eher mit C/C++ vergleichen und leider gelten deshalb auch „andere“ Regeln. Das fängt bereits damit an, dass C/C++ (nativ) eigentlich keine Strings kennt – traurig aber wahr. Wir müssen bei der Nutzung des Win32-API die „heile und unbeschwerte Welt des VBA“ verlassen und in die „Abgründe der C-Welt“ hinabsteigen.

Das Win32-API besteht aus einer Vielzahl (besser: Unmenge) von Funktionen, die im Betriebssystem fest implementiert sind. Man benutzt de facto die gleichen Funktionen wie das Betriebssystem selbst. Der Großteil der Funktionen ist auf eine Handvoll sog. Bibliotheksdateien (DLLs) verteilt (kernel32.dll, user32.dll, gdi32.dll etc.). Wir müssen „nur noch“ wissen, wo sich die jeweilige Funktion befindet und wie man sie nutzt.

Über das Win32-API (und auch andere API-Varianten) wurden dicke Bücher geschrieben. Dieses Know-how lässt sich im Rahmen dieses Workshops nicht mal annähernd erreichen. Daher mag nachfolgend einiges im Unklaren bleiben und man ggf. in Büchern oder im Internet „nachlesen“ muss – sorry!

Grundsätzlich müssen sämtliche Win32-API-Funktionen auf Modulebene deklariert werden. Dabei wird das „Grundgerüst“ der Funktion genutzt; z.B.:

Declare Function GetWindowsDirectory Lib "kernel32" Alias _
                "GetWindowsDirectoryA" _
                (ByVal lpBuffer As String, _
                 ByVal nSize As Long) As Long

Bevor nun jemand versucht, diese Deklaration abzuschreiben: Stopp! Kein Mensch tippt API-Deklarationen ab. Zum einen ist das hochgradig fehleranfällig (API-Funktionen sind case-sensitive!) und zum anderen gibt es genügend Referenzlisten.

Die Funktion GetWindowsDirectory entstammt der Bibliothek Kernel32.dll und erwartet 2 Argumente: lpBuffer und nSize. Zu beachten ist hierbei, dass fast alle Argumente bei API-Funktionen als Kopie (ByVal) übergeben werden.

Ohne Sekundärliteratur ist man jetzt schon fast verloren, denn was mag in den Argumenten stehen?

  • Das Argument lpBuffer entspricht einem dimensionierten String für die Rückgabe des gesuchten Ordners!
  • Das Argument nSize beinhaltet die Länge des Strings lpBuffer!

Da es sich (wie fast alle API-Routinen) um eine Funktion handelt, gibt sie auch etwas zurück.

In diesem Fall gibt die Funktion die Länge des Strings zurück, die für die Angabe des Windows-Ordners benötigt wurde (oder auch nicht...). War der String „zu kurz“ dimensioniert, erhalten wir die Länge zurück, die wir hätten benutzen müssen. Kam es jedoch zu einem Fehler, erhalten wir lediglich 0 als Rückgabe; sehr aussagekräftig...

Was ist also zu tun?

  • Wir müssen eine String-Variable deklarieren.
  • Diese Stringvariable muss entsprechend groß dimensioniert werden.
  • Wir übergeben der Funktion GetWindowsDirectory den dimensionierten String nebst Längenangabe.
  • Aus dem übergebenen String extrahieren wir die Anzahl Zeichen, die die Funktion als Rückgabewert meldet.

Ganz einfach, oder?

Neu ist hier das Dimensionieren von Strings, was innerhalb von VB/A unnötig ist. Wir müssen uns in der Regel nicht um Stringlängen kümmern und diese schon gar nicht explizit setzen.

Um nun eine String-Variable „aufzublasen“, müssen wir sie mit entsprechend vielen Zeichen „befüllen“. Theoretisch ist es egal mit welchem Zeichen dies erfolgt, aber es hat sich zum Quasi-Standard entwickelt, dies mit Leerzeichen oder mit dem ASCII-Zeichen für 0 zu tun.

Um von vornherein die richtige Länge nicht dem Zufall zu überlassen, gibt es auch hier eine Faustregel. Windows erlaubt grundsätzlich nur Pfadlängen von maximal 255 Zeichen. Also „blasen“ wir unseren String auf 260 Zeichen auf, und es kann nichts mehr geschehen.

Dieses Dimensionieren kann man nun C-like über eine Schleife durchführen, in dem wir einen String in 260 Schleifendurchläufen befüllen...

Dim sBuffer As String
dim iLoop   As Integer
 
For iLoop = 1 to 260
  sBuffer = sBuffer & " "
Next

...oder wir nutzen die verfügbaren VBA-Hausmittel. Die VBA-Funktionen Space() und String() helfen uns dabei, recht einfach das gewünschte Ergebnis zu erhalten.

Dim sBuffer As String
 
sBuffer = Space(260)
'bzw.
sBuffer = String(260, 0)

Also kann es losgehen:

' Die API-Variante
Function API_GetWindowsDir() As String
 
    Dim sBuffer As String
    Dim lResult As Long
 
    sBuffer = Space(260)
    lResult = GetWindowsDirectory(sBuffer, Len(sBuffer))
    If lResult <> 0 Then
        API_GetWindowsDir = Left$(sBuffer, lResult)
    End If
 
End Function

Ganz einfach, oder?

Nachdem wir jetzt herausgefunden haben, wo sich der Windows-Ordner befindet, können wir versuchen, alle INI-Dateien in diesem Ordner zu suchen. Dies erfolgt mittels VBA-Hausmitteln, dem WSH und dem Win32-API.