Das VBA-gesteuerte Packen und Archivieren von Dateien ist eine Aufgabe, der auch Access-Entwickler immer wieder begegnen. Dass man dabei keineswegs auf externe Komponenten oder Hilfsmittel angewiesen ist und mit Windows-Bordmittel auskommen kann, soll dieser Beitrag zeigen.
Beispieldatenbank
Die Beispiele dieses Artikels finden Sie in der Datenbank 1502_Zippen.mdb.
Zip-Format
Es gibt zweifellos Alternativen zum altehrwürdigen Zip-Format, denn RAR oder 7z etwa bieten erheblich bessere Kompressionsraten an. Doch diese Formate setzen voraus, dass auf dem System ein entsprechendes Archivierungsprogramm installiert ist, und bei Weitergabe einer solchen Archivdatei können Sie nicht einfach davon ausgehen, dass dies beim Adressaten der Fall ist. Mit dem Zip-Format sind Sie auf der sicheren Seite: Windows hat seit einigen Generationen einen entsprechenden Support eingebaut. über den Explorer lassen sich Zip-Dateien wie Ordner behandeln, einsehen und bearbeiten. Und was sich manuell erledigen lässt, sollte sich über irgendeinen Weg auch programmtechnisch realisieren lassen.
Shell-Funktionen
Die übliche Herangehensweise, wenn Funktionen von Windows gefragt sind, ist die Suche im API-Katalog. In unserem Fall kann darauf verzichtet werden, da Windows mit der Shell-Bibliothek eine COM-Schnittstelle zur Verfügung stellt, die alles Nötige mitbringt. Es handelt sich um die shell32.dll im Systemverzeichnis, welche sich in der Liste der Verweise von VBA namentlich als Microsoft Shell Controls And Automation ausgibt. Nachdem Sie die Bibliothek ihrem Projekt über den Verweisedialog hinzugefügt haben, finden Sie deren Klassen im Objektkatalog, wenn Sie links oben den Eintrag Shell32 auswählen (Bild 1). An den Klassenbezeichnungen, wie Folder, FolderItem und ShellFolderView, lässt sich bereits ablesen, dass es hier zuvörderst um Verzeichnisse und deren Inhalte geht. Da die Shell, wie erwähnt, eine Zip-Datei als Verzeichnis ansieht, wäre nun zu erläutern, wie man so ein Verzeichnis anspricht. Dazu gehen Sie vom Hauptobjekt der Bibliothek aus, der Shell-Klasse, welche in der Abbildung bereits markiert ist.
Bild 1: Die Shell32-Bibliothek, im VBA-Objektkatalog dargestellt
Eine Instanz dieser Klasse erhalten Sie einfach über die New-Zuweisung an eine Objektvariable:
Dim objShell As New Shell32.Shell
Hier sollte nicht unerwähnt bleiben, dass die ganze Angelegenheit auch komplett ohne einen Bibliotheksverweis auskäme. Folgender Ausdruck erzeugt nämlich ebenfalls das Shell-Objekt:
Dim objShell As Object Set objShell = _ CreateObject("Shell.Application")
Haben Sie nun das Hauptobjekt, so können Sie sogleich allerlei Methoden desselben ansprechen. So öffnet etwa die Methode objShell.FileRun den Ausführen-Dialog von Windows, oder TrayProperties den Dialog zu Einstellungen der Taskleiste. Warum Microsoft ausgerechnet solche Funktionen in die COM-Schnittstelle verbaute, ist nicht recht nachvollziehbar, da man sie programmgesteuert wohl höchst selten aufrufen wird. Sie interessieren uns auch nicht weiter. Lediglich die Funktion NameSpace ist für unsere Belange wichtig: sie allein gibt ein Folder-Objekt, also ein Verzeichnis, zurück. Dazu übergibt man ihr als Parameter den Verzeichnisnamen:
Dim objFolder As Shell32.Folder Set objFolder = objShell.NameSpace ( _ "c:\windows")
Auf den Methodennamen NameSpace würde man wahrscheinlich nicht kommen und eher so etwas wie GetFolder erwarten. Grund für die Bezeichnung ist, dass sie eben nicht nur physische Verzeichnisse ermittelt, sondern auch virtuelle, wie dies beim Zip-Archiv vorliegt. Statt des Verzeichnispfads nimmt sie etwa auch Zahlen entgegen. Versuchen Sie dies:
objShell.NameSpace(0).Title
Ergebnis wäre der String Desktop. (Title ist die Methode zum Auslesen der Bezeichnung eines Folder-Objekts.) Der komplette Pfadname ist etwas umständlicher zu ermitteln:
objShell.NameSpace(0).Self.Path
Das ergibt etwa C:\Users\André\Desktop. Sie können nun mit den Zahlenparametern experimentieren. übergeben Sie die 3, so erhalten Sie als Bezeichnung Systemsteuerung. Tatsächlich lässt sich die Systemsteuerung ja im Explorer-Baum an-zeigen. Was aber ist deren Pfad Sie erhalten den Ausdruck
::{21EC2020-3AEA-1069-A2DD-08002B30309D}
Diese GUID symbolisiert einen virtuellen Ordner. Machen Sie die Probe aufs Exempel und setzen diese GUID als String für den Pfadnamen ein:
objShell.NameSpace(“::{21EC2020-3AEA-1069-A2DD-08002B30309D}”).Title
Auch das geht und liefert uns abermals die Bezeichnung Systemsteuerung.
Zusammengefasst also nimmt NameSpace als Parameter sowohl Verzeichnis-Strings entgegen, wie auch Zahlenwerte (die sogenannten KnownFolder-IDs) und schließlich GUIDs (KnownFolder-CLSIDs) als Strings.
Wenden wir uns nach diesem Ausflug in den Shell-Namespace wieder den Zip-Archiven zu. Sie können sicher erraten, wie eine Zip-Datei als Folder-Objekt zu erhalten ist: Sie übergeben einfach deren Dateinamen. Ein Beispiel:
Set objFolder = objShell.NameSpace ( _
"d:\beispiele\test.zip")
Als nächstes interessiert uns natürlich der Inhalt eines Ordners – in diesem Fall die enthaltenen Dateien. Die Folder-Klasse sieht dafür die Auflistungs-Eigenschaft Items vor, welche sich über eine For-Next-Schleife durchlaufen lässt. Jedes Element der Items-Auflistung ist vom Typ FolderItem. Möchten Sie die Namen der Dateien im Ordner ausgeben, so käme etwa diese Routine infrage:
Dim objItm As FolderItem For Each objItm in objFolder.Items Debug.Print objItm.Name Next objItm
Auf diese Weise können Sie die in einem Zip-Archiv befindlichen Dateien inspizieren. Dabei sollte noch erwähnt werden, dass das Archiv wiederum Ordner enthalten kann. Nur dann weist die Eigenschaft IsFolder des entsprechenden FolderItems den Wert True auf, und die Eigenschaft Type gibt einen Text aus, der genau der Bezeichnung im Explorer entspricht. Je nach System kann diese Bezeichnung allerdings unterschiedlich sein. Ist etwa ein Packprogramm installiert, so könnte die Bezeichnung WinRAR ZIP-Archiv lauten.
FolderItems extrahieren
Wie jedoch lassen sich die Dateien oder Ordner des Archivs nun entpacken Im Objektkatalog findet man weder zum FolderItem, noch zum Folder-Objekt Funktionsnamen, wie Extract oder Uncompress, die dafür dienlich sein könnten. Das aber ist einleuchtend, da die Shell das Archiv als normalen Ordner ansieht und somit lediglich jene Methoden anbietet, die auch für Dateien eines Verzeichnisses gälten. Im Explorer können Sie eine Datei aus dem Archiv extrahieren, indem Sie sie entweder per Drag&Drop verschieben, oder indem Sie sie kopieren und in einen anderen Ordner einfügen. Genau das geht auch hier über die CopyHere-Funktion eines Folder-Objekts. Diese Methode erwartet als Parameter ein Folder– oder FolderItem-Objekt. Der Vorgang hat folgendermaßen auszusehen:
Sie setzen zuerst ein Folder-Objekt auf das Zielverzeichnis. Dann benutzen Sie dessen CopyHere-Methode, um in dieses Verzeichnis Dateien oder Ordner zu kopieren, wobei diese in Form von zuvor erzeugten FolderItems daher kommen müssen.
Machen wir den Test und versuchen das Archiv test.zip in einen Ordner d:\testordner zu extrahieren:
Dim objZiel As Folder Set objZiel = objShell.NameSpace( _ "d:\testordner") objZiel.CopyHere objZipFolder
Leider wird das Archiv damit nicht entpackt, sondern nur eine Kopie der Zip-Datei angelegt – eigentlich klar, da das Objekt objZipFolder ja das Archiv selbst kennzeichnet, nicht dessen Inhalt. Folglich muss hier die oben angeführte Routine zum Auslesen der FolderItems modifiziert werden:
Dim objItm As FolderItem For Each objItm in objFolder.Items objZiel.CopyHere objItm DoEvents Next objItm
Und tatsächlich: Im Zielordner laden damit die Dateien des Archivs! Sollte das Archiv Unterordner mit Dateien enthalten, so werden auch diese alle anstandslos entsprechend der Struktur angelegt. Es ist also nicht etwa nötig, diese Unterordner und Dateien in der Routine alle einzeln anzusprechen.
Wichtig ist auch die Zeile mit der DoEvents-Anweisung. Da der Shell-Prozess asynchron arbeitet, ist er unter Umständen noch mit dem Kopieren eines Items beschäftigt, während VBA bereits mit dem Code fortfährt – die CopyHere-Methode wartet nicht auf den Vollzug! Es hat sich gezeigt, dass ohne ein DoEvents manchmal Dateien unterschlagen werden.
Bei umfangreicheren Archiven werden Sie feststellen, dass sich beim Ausführen der Routine automatisch der Windows-Fortschrittsdialog öffnet. Das Shell-Objekt verhält sich also exakt gleich, wie beim manuellen Kopieren von Dateien im Explorer. In Maßen lässt sich das Verhalten jedoch über einen zweiten optionalen Parameter von CopyHere steuern, den wir bisher unterschlagen haben. Mit Options kann man eine Kombination von Steuerkonstanten übergeben, die Sie im Modul mdlZipShell der Beispieldatenbank kommentiert als Enumeration eFileOp finden. So bewirkt etwa die Angabe von eOpNoProgress, dass sich eben kein Fortschrittsdialog öffnet. Oder eOpAllowUndo weist an, dass die Operation wieder rückgängig gemacht werden kann. In unserem Fall hieße das, dass Sie im Explorer dann das Rückgängig-Symbol betätigen könnten, wodurch sich das Zielverzeichnis wieder leeren würde. Interessant ist auch die Konstante eOpNewName. Ohne deren Angabe würde eine existierende Datei mein.txt im Zielverzeichnis nicht überschrieben, sondern automatisch mit einem neuen Namen kopiert; also etwa in Kopie von mein.txt oder mein(1).txt.
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: