Datei- und Verzeichnisoperationen mit der Shell

Zu den häufigsten Aufgaben außerhalb einer Access-Datenbankanwendung gehört sicher der Zugriff auf Dateien und Verzeichnisse. Ob Sie nun externe Daten exportieren oder importieren, Dokumente fernsteuern, oder Eigenschaften von Dateien ermitteln möchten, all dies erfordert Navigation in Verzeichnissen und das Ansprechen von Dateien. Und das möglicherweise auch noch innerhalb einer Netzwerkstruktur. Die VBA-Grundfunktionen machen es Ihnen da nicht immer leicht. über die Shell von Windows kommen Sie jedoch spielend zum Ziel.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1610_ShellObjekte.accdb

VBA-Funktionen für Dateien und Verzeichnisse

Die Klasse FileSystem der VBA-Bibliothek gibt im Objektkatalog eine nur dürftige Liste von Methoden aus, über die verschiedene Datei- und Verzeichnisoperationen ausgeführt werden können. Dieses altehrwürdige Ensemble stammt noch aus Zeiten, in denen Visual Basic eine gewichtige Rolle spielte, und das war Mitte der 90er-Jahre, als noch erheblich geringere Anforderungen gestellt wurden. Seitdem hat sich am Befehlsbestand nichts geändert und die Funktionen weisen leider immer noch Mängel auf.

Die Liste ist schnell beschrieben. Zu den Verzeichnisfunktionen gehören diese:

CurDir ermittelt das aktuelle Verzeichnis. ChDir wechselt zu einem anderen, ChDrive zu einem anderen Laufwerk. Derlei braucht heute niemand mehr – es spielte eher unter DOS eine Rolle. MkDir erzeugt ein neues Verzeichnis mit dem im Parameter angegebenen. Leider funktioniert das nur dann, wenn das oberste Verzeichnis bereits existiert. Das neue kann nur eine Ebene tiefer angelegt werden. ähnliches gilt für RmDir, was ein Verzeichnis löscht. Die Anweisung klappt nicht, wenn ein Verzeichnis nicht leer ist.

Und das war’s auch schon! Die Dateimethoden beschränken sich nun auf folgende:

Kill löscht die angegebene Datei. FileCopy kopiert eine Datei in den im zweiten Parameter angegebenen Ordner. Verschieben geht nicht. Es ist nur durch die Kombination aus FileCopy und anschließendem Kill zu realisieren. FileLen gibt die Dateigröße zurück, FileDateTime das Erstelldatum, GetAttr die Zugriffsattribute.

Lesend und schreibend greifen Sie über die Open-Anweisung auf eine Datei zu. Das ist wohl von allen der wichtigste Befehl. Und schließlich lassen sich über Dir sowohl Dateien, wie auch Verzeichnisse, enumerieren oder nach ihrer Existenz befragen.

Mindestens zwei Kritikpunkte sind für sämtliche dieser Dateisystemfunktionen hervorzubringen. Zum einen scheitern sie häufig an Objekten, deren Namen in Unicode formatiert sind und Sonderzeichen enthalten. Hier ruft VBA schlicht einen Fehler aus. Zum anderen – und das ist viel schwerwiegender! – kommen sie nicht mit Netzwerkpfaden klar. Auf die Dateien eines Server in der Syntax \\serverxy\c\… besteht kein Zugriff. Hier wird dann ein nicht existierendes Verzeichnis bemängelt oder eine falsche Dateinummer gemeldet. Behelfen können Sie sich da dann nur mit zusätzlich eingerichteten Netzlaufwerken.

Einst führten diese Beschränkungen zu einer Menge Programmierlösungen unter Verwendung von Windows-API-Funktionen, die im Internet fröhliche Urstände feierten. Das aber ist inzwischen obsolet.

Die Windows-Shell-Bibliothek

Es lohnt sich uneingeschränkt, auf diese COM-Bibliothek, die grundsätzlich jedes Windows mit sich bringt, ein Auge zu werfen, denn sie ermöglicht einfache Lösungen, die auf andere Weise viel umständlicher zu erreichen wären. Sie hängen sie in ihr VBA-Projekt ein, indem Sie zunächst einen Verweis auf Microsoft Shell Controls And Automation setzen. Verantwortlich für die Bibliothek ist die System-Dll shell32. So lautet auch der Name der Bibliothek, wenn Sie sie dann unter VBA ansprechend möchten. Es gibt genau ein Objekt in ihr, das die Zentrale für alle weiteren Vorgänge darstellt, nämlich das Shell-Objekt.

Sie erzeugen das Objekt über den New-Operator:

Dim oShell As Shell32.Shell
Set oShell = New Shell32.Shell

Nun lassen sich über dieses Objekt allerlei Dinge anstellen, wie ein Blick in den Objektkatalog zeigt, nachdem Sie im Kombinationsfeld links oben Shell32 auswählten und in der Klassenliste Shell markierten. Wir lassen hier jedoch alle Methoden unbeachtet, bis auf eine sehr mächtige, die sich NameSpace nennt.

NameSpace

Die Funktion gibt zu dem im Parameter übergebenen Ausdruck ein Verzeichnisobjekt zurück. Dieses ist vom in der Verweisbibliothek definierten Typ Folder, mit dem sich anschließend weitere Operationen ausführen lassen. Als Parameter für NameSpace kommen sowohl Strings, wie auch Zahlen infrage, wie wir gleich sehen werden.

Ein kleines Bespiel macht alles deutlicher:

Dim oShell As New Shell32.Shell
Dim oFld As Shell32.Folder
Set oFld = _
    oShell.NameSpace("c:\windows")
Debug.Print oFld.Title

Im VBA-Direktfenster wird bei Ausführung dieses Schnipsels der Text Windows ausgegeben. Das Folder-Objekt oFld hat diesen Titel, der nicht mit dem Pfad zu verwechseln ist. Den erhält man erst über einen Umweg:

Debug.Print oFld.Self.Path
> c:\windows

Das ist soweit noch nicht sonderlich aufregend. Die Besonderheit der Windows-Shell liegt jedoch darin begründet, dass sie all ihre Objekte wie Verzeichnisse und Dateien behandeln kann, also auch Objekte außerhalb des Dateisystems. Der Explorer zeigt das deutlich. Deshalb kann man NameSpace auch unterschiedliche Werte für den Parameter zuweisen, eben jene, die auch der Explorer verarbeiten kann. Hier einige Beispiele für mögliche Parameter:

c:\windows\
nas\public\images
3
http://www.access-im-unternehmen.de
ftp://ftp.rz.uni-wuerzburg.de/pub
e:\dokumente\alles.zip
::{21EC2020-3AEA-1069-A2DD- _
                        08002B30309D}

Da kann die Dir-Funktion von VBA wohl nicht mithalten! Die erste Zeile bezieht sich auf den lokalen Pfad von Windows. Die zweite ist eine ein Serverpad im Netzwerk – für die Shell kein Problem! Die dritte Zeile übergäbe einfach eine Zahl. NameSpace nimmt dann an, dass es sich um einen der im System verankerten sogenannten Special Folders handelt, die jeweils eine eindeutige ID besitzen. Das sind vorgegebene Windows-Spezialordner, wie etwa der Desktop, welcher über den Parameterwert 0 anzusprechen wäre. Mit der 3 hingegen erhalten Sie als Folder.Title die Bezeichnung Systemsteuerung, denn auch die ist ein virtueller Ordner. Und was wäre deren Pfad Folder.Self.Path gibt dies aus:

::{21EC2020-3AEA-1069-A2DD- _
                        08002B30309D}

Es handelt sich hier um einen GUID-String, da es ja, im Gegensatz zum Desktop, keinen physischen Ordner für die Systemsteuerung gibt. Geben Sie diesen String einmal spaßeshalber in die Adresszeile des Explorers ein. Sie gelangen damit tatsächlich zur Systemsteuerung!

Selbst Internet-URLs kann NameSpace verarbeiten, wie die beiden folgenden Zeilen der Beispielparameter zeigen. Sowohl das HTTP-Protokoll, wie auch das FTP-Protokoll, werden klaglos angenommen. Hier kommen ebenfalls gültige Folder-Objekte zustande!

Noch krasser ist die übergabe der ZIP-Datei alles.zip. Denn auch der Explorer behandelt ZIP-Archive wie Ordner. Deshalb ist ein ZIP-Archiv automatisch auch bei NameSpace ein gültiges Ordner-Objekt.

Hier wird schon deutlich, dass die Möglichkeiten der Shell-Objekte weit über das hinausgehen, was VBA an Bordmitteln zu bieten hat.

FolderExists

Aus dem bisher Erläuterten geht die einfachste Anwendung hervor, nämlich der Test, ob ein Verzeichnis denn existiert. übergeben Sie NameSpace nämlich einen ungültigen String oder eine ungültige Zahl, so ereignet sich kein Fehler. Stattdessen ist das zurückgegeben Folder-Objekt einfach leer, hat also quasi den Wert Nothing. Das lässt sich nun leicht überprüfen, wie die Routine FolderExists in Listing 1 zeigt. Ihr übergeben Sie das fragliche Verzeichnis als Parameter. In der Objektvariablen oFld erhalten Sie aus NameSpace das Folder-Objekt. Ist dieses nicht Nothing, so existiert das Verzeichnis, womit der Rückgabewert der Funktion auf True gesetzt wird. Wohlgemerkt: Nicht nur lokale Verzeichnisse können mit dieser Funktion befragt werden, sondern ebenso Netzwerkpfade und Internetpfade!

Function FolderExists(sFolder As String) As Boolean
     Dim oFld As Shell32.Folder
     
     Set oFld = oShell.NameSpace(sFolder)
     If Not oFld Is Nothing Then FolderExists = True
End Function

Listing 1: überprüfen der Existenz eines Verzeichnisses

Nutzt man die Shell-Funktionen in mehreren Prozeduren, so ist es übrigens hilfreich, statt dem dauernden Neuerzeugen des Shell-Objekts für dieses eine gesonderte Funktion zu schreiben. Auch FolderExists verwendet eine solche (oShell), wie sie in Listing 2 abgebildet ist. Hier ist im Modulkopf die globale Variable m_Shell deklariert. In der Prozedur wird erst getestet, ob deren Inhalt leer ist. In diesem Fall wird per New eine neue Shell-Instanz erzeugt und ihr zugewiesen. Die Funktion gibt schließlich den Wert der Objektvariablen m_Shell zurück.

Public m_Shell As Shell32.Shell 
Public Function oShell() As Shell32.Shell
     If m_Shell Is Nothing Then Set m_Shell = New Shell32.Shell
     Set oShell = m_Shell
End Function

Listing 2: Funktion zum Zurückgeben des Shell-Objekts

Special Folders

Es gibt keine genaue Aufstellung von Microsoft, welche Zahlenwerte für NameSpace welchem Spezialverzeichnis zuzuordnen sind. Das variiert je nach Betriebssystem und Sprache. Bei den gebräuchlichsten stimmen sie allerdings überein.

Den Desktop erreichen Sie immer über die 0, das Startmenü über 22, oder das Systemverzeichnis über 37. Diese Werte sind im Windows-SDK in den Header-Dateien deklariert. Doch machen Sie für Ihr System doch einfach selbst die Aufstellung und verwenden die Routine StoreSpecialFolders im Modul mdlSpcFolders der Beispieldatenbank!

Die in Listing 3 dargestellte Prozedur speichert alle möglichen Werte in der Tabelle tblShellFolders, die zuerst über die DELETE-Anweisung geleert wird. Dann durchläuft eine Schleife auf den Zähler i die Werte von 0 bis 255 und testet NameSpace damit.

Sub StoreSpecialFolders()
     Dim fld As Folder
     Dim i As Long
     Dim sPath As String
     Dim rs As DAO.Recordset
     
     CurrentDb.Execute "DELETE FROM tblShellFolders"
     Set rs = CurrentDb.OpenRecordset("SELECT * FROM tblShellFolders", dbOpenDynaset)
     On Error Resume Next
     For i = 0 To 255
         Set fld = oShell.NameSpace(i)
         Err.Clear
         sPath = fld.Self.Path
         If Err.Number = 0 Then
             rs.AddNew
             rs!SFID = i
             rs![Name] = fld.Title
             rs!Path = sPath
             rs.Update
         End If
     Next i
     rs.Close
End Function

Listing 3: Alle Windows-Spezialordner kommen in die Tabelle tblShellFolders

Möchten Sie weiterlesen? Dann lösen Sie Ihr Ticket!
Hier geht es zur Bestellung des Jahresabonnements des Magazins Access [basics]:
Zur Bestellung ...
Danach greifen Sie sofort auf alle rund 400 Artikel unseres Angebots zu - auch auf diesen hier!
Oder haben Sie bereits Zugangsdaten? Dann loggen Sie sich gleich hier ein:

Schreibe einen Kommentar