Das Öffnen eines Dialogs zum Auswählen des Namens einer zu speichernden Datei erledigen Sie beispielsweise mit der API-Funktion „GetSaveFileDialog“. Diese stellen wir im vorliegenden Beitrag für 32-Bit- und 64-Bit-Office vor. Dabei weisen wir auch auf die Änderungen hin, die für das Update einer eventuell bereits bestehenden 32-Bit-Version auf die 64-Bit-Version notwendig sind.
Die API-Funktion GetSaveFileDialog ist eine Funktion der Bibliothek comdlg32.dll. Sie erwartet alle Parameter, die das Aussehen des Speichern-Dialogs beeinflussen, in Form eines Typs namens OPENFILENAME. Er heißt OPENFILENAME, weil er in gleicher Form auch für die API-Funktion zum Anzeigen eines Datei öffnen-Dialogs zum Einsatz kommt. Sie liefert den Pfad der zu speichernden Datei zurück oder eine leere Zeichenkette, falls kein Name ausgewählt wurde – zum Beispiel, weil der Benutzer den Dialog mit der Abbrechen-Schaltfläche geschlossen hat.
Die Wrapper-Funktion GetSaveFile
Vor dem Aufruf der API-Funktion GetSaveFileDialog füllt man also den Typ OPENFILENAME mit den gewünschten Einstellungen.
Da Sie das nicht jedes Mal erledigen sollen, wenn Sie diese Funktion zu einer Ihrer Anwendungen hinzufügen wollen, liefern wir eine Wrapperfunktion namens GetSaveFile. Diese sieht wie in Listing 1 aus.
Public Function GetSaveFile(Optional strStartDir As String, _ Optional strDefFileName As String, _ Optional strFilter As String = "Alle Dateien (*.*)", _ Optional strTitle As String) As String Dim udtOpenFileName As OPENFILENAME Dim strExt As String On Error GoTo Fehler If Len(strStartDir) = 0 Then strStartDir = CurrentProject.Path End If With udtOpenFileName .nStructSize = LenB(udtOpenFileName) .hwndOwner = Application.hWndAccessApp strFilter = strFilter & vbNullChar & vbNullChar .sFilter = strFilter .nFilterIndex = 1 .sInitDir = strStartDir & vbNullChar .sDlgTitle = strTitle .sFile = Space$(256) & vbNullChar .nFileSize = Len(.sFile) If Len(strDefFileName) <> 0 Then Mid(.sFile, 1) = strDefFileName End If .sFileTitle = Space$(256) & vbNullChar .nTitleSize = Len(.sFileTitle) If GetSaveFileName(udtOpenFileName) Then GetSaveFile = Left(udtOpenFileName.sFile, InStr(.sFile, vbNullChar) - 1) Else GetSaveFile = "" End If End With Ende: Exit Function Fehler: MsgBox Err.Description, vbCritical, "GetSaveFileName" Resume Ende End Function
Listing 1: Wrapperfunktion für die API-Funktion GetSaveFileName
Dies sind die Parameter:
- strStartDir: Hier geben Sie das Verzeichnis an, das beim Öffnen des Dialogs vorausgewählt werden soll.
- strDefFileName: Vordefinierter Dateiname, der beim Öffnen des Dialogs im Feld Dateiname eingetragen werden soll.
- strFilter: Ein Ausdruck, der angibt, welche Dateitypen als Speichername verwendet werden dürfen.
- strTitle: Titel des Dialogs
Ablauf von GetSaveFile
Der Wrapperfunktion für die API-Funktion GetSaveFileName haben wir einen etwas verkürzten Namen gegeben – eben GetSaveFile. Sie definiert als Erstes eine Variable namens udtOpenFileName des Typs OPENFILENAME. Wie dieser genau aussieht und deklariert wird, zeigen wir weiter unten.
Dieser Typ arbeitet wie eine Klasse und hat verschiedene Eigenschaften, die wir mit der Funktion GetSaveFile füllen. nStructSize nimmt beispielsweise die Größe des Typs udtOpenFileName entgegen, die wir mit der Funktion LenB ermitteln. Das ist übrigens auch eine der Änderungen der 64-Bit-Version gegenüber der 32-Bit-Version – früher wurde hier die Funktion Len verwendet. Len ermittelt die Anzahl der Zeichen einer Zeichenkette, LenB die Anzahl der Bytes dieser Zeichenkette. hwndOwner füllen wir mit dem Handle des aktuellen Fensters. Den Filter aus dem Parameter strFilter erweitern wir noch um zwei vbNullChar, bevor wir ihn der Eigenschaft sFilter zuweisen. Gültige Werte dazu schauen wir uns gleich im Anschluss an. Welcher der Filter voreingestellt wird, legt die Funktion für die Eigenschaft nFilterIndex fest, in diesem Fall mit dem Wert 1.
Das mit dem Parameter strStartDir übergebene Startverzeichnis ergänzt die Funktion ebenfalls um vbNullChar und schreibt sie dann in die Eigenschaft sInitDir. Der Titel für den Dialog landet unbehandelt in sDlgTitle.
In sFile trägt die Funktion eine leere Zeichenkette mit einer Länge von 256 Zeichen und einem abschließenden vbNullChar ein. nFileSize erhält die Länge von sFile.
Wenn der Aufruf einen Wert für den Parameter strDef-FileName enthält, also den voreinzustellenden Speichernamen, wird dieser vorn in der Eigenschaft sFile eingesetzt. Damit die Länge der darin enthaltenen Zeichenkette nicht verändert wird, verwenden wir dazu die Mid-Funktion in einer eher unbekannten Art. Der Aufruf Mid(.sFile, 1) = strDefFileName sorgt dafür, dass der Inhalt von strDefFileName die entsprechenden Zeichen von .sFile ab der ersten Position überschreibt.
sFileTitle soll den ermittelten Namen ohne Verzeichnis aufnehmen und wird mit 256 Leerzeichen plus abschließendem vbNullChar gefüllt. Auch die Länge diese Zeichenkette landet in udtOpenFileName, und zwar in der Eigenschaft nTitleSize.
Nachdem alle relevanten Eigenschaften von udtOpenFileName gefüllt sind, ruft die Funktion die API-Funktion GetSaveFileName mit udtOpenFileName als Parameter auf. Liefert diese Funktion den Wert True zurück, was der Fall ist, wenn der Benutzer die Schaltfläche OK betätigt, schreibt die Funktion den Inhalt von sFile aus udtOpenFileName in den Rückgabewert der Funktion. Anderenfalls liefert die Funktion eine leere Zeichenkette zurück.
Deklaration der API-Funktion GetSaveFileName
Wichtig für die Einsatzbereitschaft der Funktion GetSaveFileName unter 32-Bit- und 64-Bit-Office ist, dass Sie für beide Varianten die richtige Deklaration bereitstellen. Genau genommen ist es sogar so, dass Sie nur prüfen müssen, ob Sie VBA 6.x oder VBA 7.x verwenden. VBA 6.0 kam mit älteren Office-Versionen bis 2007. VBA 7 wurde eingeführt, um die Kompatibilität mit 64-Bit zu sichern. Grundsätzlich müssen wir also prüfen, welche Access-Version auf dem Zielrechner vorliegt und nicht, ob diese mit 32-Bit oder 64-Bit kompatibel ist (mehr dazu im Beitrag 32-Bit, 64-Bit, VBA-Version und Co. (www.access-im-unternehmen.de/1322).
Ausgangspunkt für diesen Beitrag war eine Version der Deklaration des Typen OPENFILENAME und der Funktion GetSaveFileName, die unter VBA 6.x erstellt wurde und dementsprechend mit dem Datentyp Long auch für Zeiger und Handles arbeitet und die in der Deklaration der API-Funktion nicht das Schlüsselwort PtrSafe verwendet.
Diese funktioniert auch noch unter VBA 7.x, allerdings nur, wenn Office in der 32-Bit-Version vorliegt. Wir wollen nun eine Version der Deklaration erschaffen, die noch kompatibel mit VBA 6.x ist, aber auch mit VBA 7.x und somit auch mit 64-Bit. Dazu benötigen wir zwei Varianten, die wir in einer If…Then-Bedingung unterscheiden. Weil wir in der Bedingung die Kompilierkonstante VBA7 nutzen, müssen wir den Zeilen der Bedingung das Raute-Zeichen (#) voranstellen. Damit werden die innerhalb der Bedingung aufgeführten Zeilen nur im Code berücksichtigt, wenn die jeweilige Bedingung wahr ist.
In Listing 2 prüfen wir in der If…Then-Bedingung den Wert der Kompilierkonstanten VBA7 im If-Teil der Bedingung. Dieser Teil enthält also den Code, der ausgeführt werden soll, wenn wir VBA 7.x nutzen. Der Else-Teil führt den Code aus, der mit VBA 6.x kompatibel ist. Den Code aus dem Else-Teil kennen Sie vermutlich aus älteren Beiträgen. Im If-Teil für VBA 7.x haben wir kleine Änderungen vorgenommen:
#If VBA7 Then Private Type OPENFILENAME nStructSize As Long hwndOwner As LongPtr hInstance As LongPtr sFilter As String sCustomFilter As String nCustFilterSize As Long nFilterIndex As Long sFile As String nFileSize As Long sFileTitle As String nTitleSize As Long sInitDir As String sDlgTitle As String Flags As Long nFileOffset As Integer nFileExt As Integer sDefFileExt As String nCustData As Long fnHook As LongPtr sTemplateName As String End Type Private Declare PtrSafe Function GetSaveFileName Lib "comdlg32.dll" Alias _ "GetSaveFileNameA" (pOpenfilename As OPENFILENAME) As Long #Else Private Type OPENFILENAME nStructSize As Long hwndOwner As Long hInstance As Long sFilter As String sCustomFilter As String nCustFilterSize As Long nFilterIndex As Long sFile As String nFileSize As Long sFileTitle As String nTitleSize As Long sInitDir As String sDlgTitle As String Flags As Long nFileOffset As Integer nFileExt As Integer sDefFileExt As String nCustData As Long fnHook As Long sTemplateName As String End Type Private Declare Function GetSaveFileName Lib "comdlg32.dll" _ Alias "GetSaveFileNameA" (pOpenfilename As OPENFILENAME) As Long #End If
Listing 2: Die Deklaration der API-Funktion GetSaveFileName plus dem Typ OPENFILENAME
- In der Deklaration des Typs OPENFILENAME haben wir für die drei Eigenschaften hwndOwner, hInstance und fnHook den Datentyp Long durch LongPtr ersetzt. Diese drei Eigenschaften enthalten Zeiger, Handle und Hook – nur diese müssen geändert werden, die übrigen Long-Eigenschaften, die reine Zahlenwerte enthalten, bleiben erhalten.
- In der Deklaration der API-Funktion GetSaveFileName stellen wir dem Schlüsselwort Function das Wort PtrSafe voran.
- Die letzte Änderung haben wir bereits besprochen – das Ändern der Funktion Len zur Berechnung der Größe von OPENFILENAME in LenB.
Wenn Sie sicher sind, dass Ihre Anwendung nur unter Access ab Version 2010 eingesetzt wird, können Sie natürlich auch nur den Teil im If-Teil der Bedingung abbilden und die Bedingung weglassen. Das heißt, Sie müssen nur eventuell vorhandenen API-Code für VBA 6.x nach VBA 7.x migrieren.
Die Wrapper-Funktion GetSaveFile verwenden
Nun schauen wir uns noch einige Beispiele für den Aufruf von GetSaveFile an, die Sie jeweils im Direktbereich absetzen können.
Die einfachste Variante lautet:
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo