PDF ansehen - Access [basics]
Transcription
PDF ansehen - Access [basics]
ACCESS LÖSUNGEN ZIPPEN MIT BORDMITTELN BASICS Zippen mit Bordmitteln 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 ShellBibliothek eine COM-Schnittstelle zur Verfügung stellt, die alles Nötige mitbringt. Es handelt sich um die shell32.dll im Systemverzeichnis, www.access-basics.de 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. 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 ShellObjekt: Dim objShell As Object Set objShell = _ CreateObject("Shell.Application") Bild 1: Die Shell32-Bibliothek, im VBAObjektkatalog dargestellt 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 nachvollzieh- Seite 3 ACCESS LÖSUNGEN ZIPPEN MIT BORDMITTELN BASICS bar, 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: ::{21EC2020-3AEA-1069-A2DD- Dim objItm As FolderItem 08002B30309D} For Each objItm in objFolder.Items Diese GUID symbolisiert einen virtuellen Ordner. Machen Sie die Probe aufs Exempel und setzen diese GUID als String für den Pfadnamen ein: Next objItm Debug.Print objItm.Name ? objShell.NameSpace("::{21EC20203AEA-1069-A2DD-08002B30309D}").Title 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 anzeigen. Was aber ist deren Pfad? Sie erhalten den Ausdruck www.access-basics.de 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: 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: Seite 4 ACCESS LÖSUNGEN ZIPPEN MIT BORDMITTELN BASICS 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: 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. 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. 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. Wichtig ist auch die Zeile mit der DoEvents-Anweisung. Da der Shell- Die komplette Routine zum Entpacken eines Zip-Archivs finden Sie 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 www.access-basics.de in der Beispieldatenbank unter der Funktion UnZipFile. Sie übergeben ihr einfach den Pfadnamen der ZipDatei und das Zielverzeichnis. Bei erfolgtem Entpacken gibt die Funktion ein True zurück. Wie Sie sehen, kommt man über die Shell-Bibliothek mit nur wenigen Code-Zeilen zum Ergebnis, und externe Zusatzkomponenten sind überflüssig. Zip-Archive erzeugen Zwar gibt uns Windows die Möglichkeit, Zip-Dateien zu entpacken, aber das Anlegen solcher scheint nicht vorgesehen zu sein. Ein Rechtsklick auf den freien Bereich eines Ordners im Explorer zeigt im Kontextmenü unter Neu... keinen Eintrag für das Erstellen eines Zip-komprimierten Ordners. Tatsächlich gibt es keinerlei Methoden in der Shell, um das zu bewerkstelligen. Zum Glück ist eine Zip-Datei prinzipiell eine ziemlich einfache Sache, die sich auch über einen Workaround anlegen lässt. Nehmen Sie eine beliebige Zip-Datei und löschen aus ihr sämtliche Dateien. Das Archiv existiert dann immer noch, hat nun aber eine Größe von nur 23 Byte. Dieses Grundgerüst kann auch über VBA angelegt werden: Dim arrZip(22) As Byte arrZip(0) = 80 'P arrZip(1) = 75 'K arrZip(2) = 5 arrZip(3) = 6 Sie legen ein Byte-Array an und füllen die ersten vier Elemente mit den Vorgabewerten für eine Zip-Datei. Die restlichen Bytes enthalten Nullen. Seite 5 ACCESS LÖSUNGEN ZIPPEN MIT BORDMITTELN BASICS Das Array muss nun nur noch in eine Datei gespeichert werden: Dim objZip As Shell32.Folder Dim objSource As Folder Dim objItem As FolderItem Set objZip = objShell.NameSpace("d:\test\xy.zip") Set objSource = objShell.NameSpace("d:\test\") Set objItem = objSource.Items.Item("mein.txt") objZip.CopyHere objItem und CreateZipFromFolder. Der ersten übergeben Sie als Parameter den Pfadnamen der zu erzeugenden Zip-Datei und Open "d:\test\xy.zip" For Binary alle hinzuzufügenden DateiAs #1 en in einem String-Array, der Listing 1: Hinzufügen von Dateien zum Zip-Archiv Put F, , arrZip zweiten stattdessen nur den Close #1 würde die Datei mein.txt des QuellPfadnamen eines Ordners, den verzeichnisses d:\test\ über die VariSie im Archiv haben wollen. Beide Im Testordner wird im Explorer jetzt able objItem in das Archiv wandern. Prozeduren werden vom Formular für die Datei xy.zip die korrekte BefrmZipper verwendet, das eine Oberzeichnung Zip-Archiv angezeigt. Sollten Sie in einer Schleife mehrere fläche für sie bereitstellt. Dateien in das Archiv übernehmen, Das weitere Vorgehen können Sie so sollten Sie auch hier ein DoEvents Zippen über Formular sich denken. Die angelegte Zip-Datei einfügen. Sonst kommt es vor, Bild 2 zeigt Sie das Formular frmZipwird über das Shell-Objekt als Folder dass im Archiv anschließend einige per der Beispieldatenbank nach dem behandelt und über dessen CopyDateien fehlen. Auch sonst gilt, etwa Öffnen. Im obersten Textfeld geben Here-Methode Dateien hinzugefügt. für die Optionen der CopyHere-MeSie den Pfadnamen der Zip-Datei an, Eine rudimentäre Routine könnte so thode, dasselbe, wie für die Routine die Sie erzeugen wollen. Dafür könaussehen: zum Entpacken des Archivs. nen Sie auch die Schaltfläche rechts daneben verwenden. Sie öffnet einen Dim objZip As Shell32.Folder Ausführlichere Routinen zum ErzeuDatei speichern-Dialog, in dem der Dim objSource As Folder gen von Zip-Archiven finden Sie in Name AB_Test.zip auf das aktuelle Set objZip = objShell.NameSpace( _ der Beispieldatenbank in Gestalt Datenbankverzeichnis voreinge"d:\test\xy.zip") der beiden Prozeduren CreateZip stellt ist, wie in Bild 3. Diesen Dialog Set objSource = objShell.NameSpace( _ "c:\windows\") objZip.CopyHere objSource Führen Sie diesen Code besser nicht aus! Er kopiert den gesamten Inhalt des Windows-Ordners in das ZipArchiv xy.zip. Deutlich machen soll er nur, dass die Shell sich hier genauso verhält, wie beim Entpacken. Komplette Ordnerstrukturen können mit nur einer Zeile in ein Archiv verfrachtet werden. Möchten Sie nur einzelne Dateien in das Archiv übernehmen, so benötigen Sie statt des Forder-Objekts ein FolderItem-Pendant. Eine Erweiterung der Routine käme etwa wie in Listing 1 daher (Ausschnitt). Dort www.access-basics.de Bild 2: Das Formular frmZipper für die Verwaltung von Zip-Archiven Seite 6 ACCESS LÖSUNGEN ZIPPEN MIT BORDMITTELN BASICS erzeugen Sie über das FileDialogObjekt von Office. Der hinter der Schaltfläche liegende Code steht in Listing 2. Falls Sie nicht den Abbrechen-Button betätigen, wird der gewählte Dateiname in das Textfeld txtZip übertragen. Darunter befindet sich ein Listenfeld, welches die dem Zip-Archiv hinzuzufügenden Dateien oder Ordner aufnimmt. Diese öffnen Sie über die beiden Schaltflächen rechts. Der mit D... bezeichnete Button lässt die Auswahl von Dateien zu, der mit O... beschriftete öffnet einen Ordner-auswahldialog (siehe Bild 4). Während für die Dateiauswahl wieder der Office-FileDialog in ähnlicher Form wie im Listing zum Einsatz kommt, wird zur Ordnerauswahl eine Methode BrowseForFolder des Shell-Objekts verwendet – denn die Office-Bibliothek kennt keinen solchen Auswahldialog. Wenn Sie eventuell den häufig benutzten und umfangreichen APICode zum Anzeigen dieses Dialogs kennen, werden Sie sich vielleicht über den überschaubaren Code in Listing 3 freuen. Der BrowseForFolder-Methode des Shell-Objekts übergibt man ein Fenster-Handle oder 0, den Titel des Dialogs, eine Bild 3: Die Datei speichern-Oberfläche des Office-FileDialog-Objekts Private Sub cmdSelect_Click() Dim oDlg As Office.FileDialog Set oDlg = Application.FileDialog(msoFileDialogSaveAs) With oDlg .Title = "Dateiname für ZIP-Archiv" .InitialView = msoFileDialogViewDetails .AllowMultiSelect = False .InitialFileName = CurrentProject.Path & "\AB_Test.zip" If .Show Then Me!txtZip = .SelectedItems(1) End If End With End Sub Listing 2: Routine zum Anzeigen des Speichern unter-Dialogs von Office Konstante, die das Erscheinungsbild bestimmt, und schließlich ein Folder- Objekt, das die oberste Ebene für den Verzeichnisbaum einstellt. Hier Function BrowseFolder() As String Dim objShell As New Shell32.Shell Dim objRootFolder As Shell32.Folder Dim objFolder As Shell32.Folder Set objRootFolder = objShell.NameSpace("Desktop") Set objFolder = objShell.BrowseForFolder(hWndAccessApp, _ "Dateiname für ZIP-Archiv:", &H40, objRootFolder) If objFolder Is Nothing Then Exit Function BrowseFolder = objFolder.Self.Path End Function Bild 4: Shell-Verzeichnisauswahldialog www.access-basics.de Listing 3: Routine zur Anzeige des Ordnerauswahldialogs über das Shell-Objekt Seite 7 ACCESS LÖSUNGEN ZIPPEN MIT BORDMITTELN BASICS wird der Desktop angegeben, während in der Abbildung als Root der Computer steht, was durch Angabe des Werts 17, statt "Desktop", für den Shell-NameSpace gelingt. Wenn Sie eine Übersicht über die Zahlenwerte haben möchten, die für den NameSpace möglich sind, so rufen Sie die Prozedur EnumNameSpaces im Modul mdlZipShell aus dem VBA-Direktfenster heraus auf. Sie gibt alle Werte aus, die auf Ihrem System angegeben werden können, sowie die Bezeichnung und den Pfad des Ordners, die, wie bereits erwähnt, auch virtueller Natur sein können, was an GUIDs für den Pfad deutlich wird. Bis hierhin wurden nur benötigte Informationen für die Erstellung der Zip-Datei gesammelt. Ein Klick auf Zip-Archiv erzeugen im Formular erst stößt die Prozeduren CreateZip oder CreateZipFromFolder an, je nachdem, ob im Listenfeld ein Ordnerpfad steht, oder eine Liste von Dateien. Im letzteren Fall durchläuft die Prozedur cmdCreateZip_Click alle Einträge des Listenfelds und speichert sie in einem StringArray, welches anschließend der CreateZip-Routine übergeben wird. Mit dieser hat es eine Besonderheit auf sich. Sie erstellt nicht grundsätzlich eine Zip-Datei neu, sondern nur dann, wenn für den Parameter Overwrite ein True angegeben wird. Ansonsten nutzt Sie ein eventuell bereits vorhan- www.access-basics.de denes Archiv gleichen Namens im Zielverzeichnis. Das bedeutet, dass Sie über diese Routine einem Archiv auch weitere Dateien hinzufügen können. Und weiterhin, dass es somit auch zu Kollisionen kommen kann, wenn im Archiv schon eine gleichnamige Datei existiert. Um diesen Konflikt brauchen Sie sich indessen nicht zu kümmern. Die Shell erkennt das selbst und konfrontiert Sie mit dem Dialog aus Bild 5, in dem Sie aufgefordert werden zu entscheiden, wie weiter verfahren werden soll. Das Auftauchen des Dialogs können Sie übrigens über keinen Optionsparameter der CopyHereMethode unterbinden. Das Formular stellt für CreateZip und Overwrite allerdings True ein, wodurch ein eventuell existierendes Archiv ohne Nachfrage gelöscht und neu erstellt wird. Demnach kann es auch nicht zur Anzeige des Konfliktdialogs kommen. Denn dieser hat einen Pferdefuß: Obwohl er noch angezeigt wird, hält die Shell den CopyHere-Vorgang nicht an und VBA fährt asynchron mit der Ausführung des Codes fort. So könnte es dazu kommen, dass die Routine bereits fertig und das Archiv damit angelegt ist, obwohl die Entscheidung über das Ersetzen noch gar nicht gefallen ist. Zwar modifiziert der Shell-Prozess das Archiv dann noch nachträglich, aber eine Kontrolle des Vorgangs über VBA ist nicht möglich. Nach dem Erzeugen der Zip-Datei öffnet das Formular diese noch zur Kontrolle über die ShellExecuteMethode des Shell-Objekts. Was hier passiert hängt von Ihrem System ab. Die Methode tut das Gleiche, wie ein Doppelklick auf die Datei im Explorer. Haben Sie etwa WinRar oder 7-Zip installiert, so werden diese wohl mit der Aufgabe des Öffnens betraut. Wenn Sie die Module der Beispieldatenbank in eigene Anwendungen einbauen möchten, so können Sie selbstverständlich die Zeilen mit dem ShellExecute auch auskommentieren. Bild 5: Das Shell-Objekt fragt vor dem Überschreiben einer Datei im Zip-Archiv sicherheitshalber nach Ist das Archiv erstellt, so ändert sich der Inhalt der Textfelder im Formular wie in Bild 6. In die unteren beiden ist nun gleich der Name der erstellten Zip-Datei eingetragen und zusätzlich noch ein Ordner angegeben, in den Sie die Datei sogleich wieder entpacken könnten. Dabei handelt sich um den Ordner, in dem sich auch die Zip-Datei befindet. Ein Klick auf Zip-Archiv entpacken extrahiert die Dateien Seite 8 ACCESS LÖSUNGEN ZIPPEN MIT BORDMITTELN BASICS Bild 6: Das Formular frmZipper nach dem Erstellen der Archivdatei im Archiv dann in dieses Verzeichnis. Natürlich kann es auch dabei wieder zur Anzeige des Konfliktdialogs kommen. Der der Schaltfläche hinterlegte Code ruft lediglich die schon angesprochene Prozedur UnzipFile auf. Nach dem Entpacken wird allerdings zur Kontrolle noch der Zielordner im Explorer geöffnet. Das lässt sich abermals mit einer unscheinbaren Methode Explore des Shell-Objekts erledigen. Ihr übergibt man einfach als String den Verzeichnispfad: objShell.Explore CStr(Me!txtOutDir) Die ausdrückliche Konvertierung des Textfeldinhalts in einen OLEkonformen String über CStr ist übrigens notwendig. Ohne diese kann die Shell mit dem Ausdruck nichts anfangen. www.access-basics.de Vielleicht fällt Ihnen bei der Durchsicht der beiden Routinen zum Zippen noch auf, dass diese am Ende die Hilfsfunktion WaitForFileReady aufrufen. Mit ihr soll sichergestellt werden, dass sich die Routinen erst dann beenden, wenn das Archiv wirklich fertiggestellt ist. Da ja CopyHere asynchron arbeitet, kann etwa ein Archivierprogramm die Zip-Datei noch nicht öffnen, solange die Shell es im Zugriff hat. In Listing 4 finden Sie diese Hilfsfunktion dargestellt. Ihr Kern ist die Zeile für den Zugriff auf die Datei per Open-Anweisung. Diese ist mit den Parametern Access Read Write Lock Read Write ausgestattet. Solange die Shell die Datei noch bearbeitet, wird dieser Zugriffsmodus wegen Dateisperrung fehlschlagen, was durch die Fehlerbehandlung abgefangen wird. Die Do-Loop-Schleife wird erst dann verlassen, wenn dieser Fehler nicht mehr auftritt. Damit die Schleife unter Umständen nicht endlos durchläuft, ist zusätzlich eine Timer-Funktion eingebaut, die sie nach 30 Sekunden (wählbar) auf jeden Fall unterbricht. Bliebe noch die Schaltfläche mit der Aufschrift Zip erzeugen und in Anlage speichern des Formulars. Im Prinzip löst diese nichts anderes aus, als die darüber liegende. Die ZipDatei wird auf gleiche Weise erstellt. Nur wird diese anschließend als Anlagedatensatz in die Tabelle MSys- Function WaitForFileReady(ByVal FileName As String, Optional TimeOut As Single = 30) Dim F As Integer Dim T As Single Dim bOK As Boolean F = FreeFile T = VBA.Timer On Error Resume Next Do Sleep 50 DoEvents Err.Clear Open FileName For Binary Access Read Write Lock Read Write As F If Err.Number = 0 Then bOK = True Close F Loop Until ((VBA.Timer - T) >= TimeOut) Or bOK WaitForFileReady = (VBA.Timer - T) < TimeOut End Function Listing 4: Diese Funktion wird erst beendet, wenn die übergebene Datei frei ist Seite 9 ACCESS LÖSUNGEN ZIPPEN MIT BORDMITTELN BASICS Resources aufgenommen, wenn Sie mindestens Access 2010 verwenden, und wieder gelöscht. Diese Systemtabelle ist in jeder unter Access 2010 ff. erstellten Datenbank mit am Start und an sich für die Aufnahme von Bildern für Formulare gedacht. Da sie nicht schreibgeschützt ist, lässt sie sich genauso gut für andere Zwecke missbrauchen. bringen, kann nun mit dem Rüstzeug dieses Beitrags realisiert werden. Der Code für das Speichern der ZipDatei im Anlagefeld über DAO soll hier nicht weiter interessieren. Sie finden ähnlichen Code auch an anderer Stelle. Statt der MSysResources können Sie natürlich auch eine eigene Tabelle mit einem Anlagefeld verwenden. Ändern Sie dann die Routine cmdZipAttachment_Click entsprechend ab. In der Sicherungsroutine werden die gefragten Datenbankobjekte als Textdateien exportiert, aber deren Dateinamen zusätzlich in ein String-Array arrFiles aufgenommen. Das ist der einzige Unterschied. Das Array wird am Schluss für die schon bekannte Prozedur CreateZip benötigt, die aus den Dateien ein Archiv braut. Ist dieses erstellt, werden die Textdateien in einer Schleife per Kill-Anweisung wieder gelöscht. Datensicherung zippen In der letzten Ausgabe 01/2015 von Access [basics] ging es im Beitrag Datenbanken und Objekte sichern unter anderem darum, die einzelnen Objekte einer Datenbank, also Formulare, Berichte, Module, et cetera, über die versteckte Access-Methode SaveAsText in einzelne Textdateien zu sichern, aus denen sie sich später wiederherstellen lassen. Bei umfangreichen Datenbanken haben Sie dann nach Durchlaufen der Sicherungsroutine einen Haufen Textdateien erzeugt. Die dort angestellte Überlegung, diese Dateien stattdessen platzsparend in einem Zip-Archiv unter- Das Modul mdlSaveObjects ist in dieser Beispieldatenbank fast identisch zu der des erwähnten Beitrags. Nur heißt die Routine zum Sichern der Objekte hier ObjektSicherungZip, die zum Einlesen SicherungWiederherstellenZip. Beim Test der Routine kam es allerdings zu Fehlern. Ein oder mehrere der Fehlerdialoge aus Bild 7 poppten auf. Das sind Meldungen der Shell auf die Anweisung CopyHere hin, die von VBA leider nicht abgefangen werden können. Wieder einmal ist das der asynchronen Verarbeitung geschuldet. Das Verhalten konnte erst korrigiert werden, nachdem in die Schleife zum Hinzufügen von Dateien zum Archiv in der Funktion CreateZip eine Verzögerung von 100 ms mit der API-Anweisung Sleep eingebaut wurde. Es hängt von der Performance Ihres Systems ab, ob Sie diese Zeile benötigen und wie groß die Verzögerung sein muss, damit die Fehlermeldungen nicht mehr auftreten. Umgekehrt geht die Wiederherstellen-Routine vor. Zunächst entpackt sie das als Parameter übergebene Sicherungsarchiv ZipFile mit UnzipFile in ein neu angelegtes temporäres Verzeichnis \temp unterhalb des Anwendungsverzeichnisses. Aus dem Archiv werden dann die einzelnen Textdateien mit LoadFromText gegebenenfalls wieder in Datenbankobjekte umgewandelt, wobei die Routine für jedes einzelne Objekt gesondert nachfragt. Ob wiederhergestellt, oder nicht, die Textdatei wird im Anschluss sogleich gelöscht. Am Ende der Schleife ist das temporäre Verzeichnis damit leer und kann mit der RmDir-Anweisung selbst gelöscht werden – sie funktioniert nur, wenn das angegebene Verzeichnis keinen Inhalt hat. Gegenüber der ursprünglichen Version hat sich dieses Sicherungsmodul nur um wenige Zeilen vergrößert, da hauptsächlich die Zip-Funktionen des Moduls mdlZipShell angesprochen werden. Bild 7: Fehlermeldung der Shell beim Hinzufügen von Dateien und dabei verzögerter Aktualisierung des Zip-Archivs www.access-basics.de Seite 10