Kennen Sie das auch Sie haben eine schicke Datenbank mit Access 2007 oder 2010 erstellt, diese mit einem Ribbon und Schaltflächen mit Icons ausgestattet und nun kommt ein Kunde und möchte diese Datenbank unter Access 2003 einsetzen. Da kommt schon ein wenig Aufwand auf Sie zu! Deshalb nehmen wir ein solches Downgrade im vorliegenden Beitrag einmal unter die Lupe.
Ausgangspunkt ist die Datenbank KVA.accdb aus dem Beitrag Kunden, Verträge und Anschreiben verwalten (www.access-im-unternehmen.de/854). Diese Anwendung wurde sogar unter Access 2010 erstellt und soll nun, soweit möglich, unter Access 2003 lauffähig gemacht werden. Damit dies gelingt, muss die Anwendung zunächst einmal um solche Elemente erleichtert werden, die sogar ein Speichern im Format von Access 2002/2003 unmöglich machen.
Ob die Datenbank solche Elemente enthält, finden Sie etwa unter Access 2010 heraus, indem Sie auf Datei klicken, dann im Backstage-Bereich auf Speichern und Veröffentlichen und schließlich auf Access 2002-2003-Datenbank (*.mdb) – s. Bild 1. Entweder führen Sie hier einen Doppelklick aus oder Sie betätigen nach der Auswahl die Schaltfläche Speichern unter.
Bild 1: Speichern einer Datenbank im Format von Access 2002/2003
Unter Access 2007 verwenden Sie dazu den Eintrag Speichern unter|Access 2002 – 2003-Datenbank des Office-Menüs (s. Bild 2).
Bild 2: Datenbank von Access 2007 aus in anderen Formaten speichern
Tendenziell enthält eine Datenbank unter Access 2007 oder 2010 Elemente, die unter Access 2002/2003 noch nicht bekannt waren.
Ist dies der Fall, meldet Access den Fehler aus Bild 3. Hier werden gleich einige potenzielle Probleme genannt – unter Access 2007 etwa Anlagefelder oder mehrwertige Felder; unter Access 2010 kommen noch Elemente wie Datenmakros, neue Verschlüsselungstypen oder Navigationssteuerelemente hinzu.
Bild 3: Fehlermeldung beim Versuch, eine Datenbank mit reinen Access 2007/2010-Features in eine ältere Version umzuwandeln
Während Sie relativ sicher sein können, dass wir in Access im Unternehmen keine mehrwertigen Felder einsetzen, so sieht dies bei Anlagefeldern anders aus: Wenn Sie mit Access 2010 arbeiten und etwa Bilder in Schaltflächen darstellen möchten, kommen Sie nicht um den Einsatz einer Systemtabelle namens MSysResources herum (die standardmäßig ausgeblendet ist – machen Sie diese unter Access 2010 durch die Aktivierung der Optionen Ausgeblendete Objekte anzeigen und Systemobjekte anzeigen sichtbar), die wiederum Bilder in einem Anlagefeld speichert.
Diese Bilddateien können so bequem als Bilder für Bildsteuerelemente oder Schaltflächen ausgewählt werden.
Sollte diese Meldung erscheinen, beginnt die Arbeit also gleich in der Access-Version, mit der Sie die Anwendung erstellt haben. Sie müssen hier die Tabellen ausfindig machen, die Felder vom Typ Anlage enthalten.
Je nachdem, wie lange Sie die Anwendung bereits entwickeln und wie umfangreich diese geworden ist, haben Sie möglicherweise keinen Überblick mehr, wo sich die Felder mit dem betreffenden Datentyp befinden.
Wollen Sie nun alle Tabellen manuell durchsuchen oder lieber einen kleinen Codeschnipsel programmieren, der alle Anlage-Felder (und gegebenenfalls auch die mehrwertigen Felder) ausgibt Wir entscheiden uns natürlich für letztere Variante, da diese auch zuverlässiger arbeiten dürfte.
Wechseln Sie also zum VBA-Editor und legen Sie in einem geeigneten Modul, beispielsweise mdlTools, eine neue Prozedur namens NeueDatentypenFinden an. Grundsätzlich soll die Prozedur alle Tabellen und alle darin enthaltenen Felder durchlaufen und die Datentypen auf die neuen Typen prüfen.
Die Type-Eigenschaft eines Field-Elements eines TableDef-Elements liefert aber Zahlenwerte. Und wo erhalten wir Informationen über die Konstanten zu diesen Zahlenwerten Im Objektkatalog. Diesen öffnen Sie mit der Taste F2 und suchen nach dem Begriff Enum.
Darunter finden Sie dann alle möglichen Enumerationen wie DatabaseTypeEnum, FieldAttributeEnum oder auch die gesuchte DataTypeEnum. Klicken Sie auf DataTypeEnum, zeigt der Objektkatalog wie in Bild 4 die verfügbaren Datentypen samt Zahlenwert an.
Bild 4: Objektkatalog mit Felddatentypen
Die neuen Datentypen sind mit Zahlenwerten größer 100 versehen – dbAttachment hat etwa den Wert 101. Also soll die Prozedur einfach alle Feldnamen ausgeben, deren Felddatentyp einen Zahlenwert größer als 100 aufweist:
Public Sub NeueFelddatentypenFinden() Dim db As DAO.Database Dim tdf As DAO.TableDef Dim fld As DAO.Field Set db = CurrentDb For Each tdf In db.TableDefs For Each fld In tdf.Fields If fld.Type > 100 Then Debug.Print tdf.Name, fld.Name, fld.Type End If Next fld Next tdf Set db = Nothing End Sub
Die Prozedur durchläuft nun alle Tabellen (also TableDef-Objekte) der TableDefs-Auflistung der Datenbank und für jede Tabelle alle Felder (also Field-Objekte) der Fields-Auflistung der aktuellen Tabelle. Weist die Type-Eigenschaft einen Wert größer als 100 auf, gibt die Prozedur Tabellenname, Feldname und Datentyp im Direktfenster aus.
Bei der Beispieldatenbank kommen wir glimpflich davon: Das einzige Anlagefeld heißt Data und stammt aus der bereits erwähnten Tabelle MSysResources:
MSysResources Data 101
Im Beispiel der Datenbank KVA.accdb ist allerdings möglicherweise eine zusätzliche Untersuchung notwendig: Die Anwendung verwendet ja ein Backend, in dem sich auch noch Tabellen befinden.
Auch diese müssen Sie selbstverständlich nach entsprechenden neuen Felddatentypen untersuchen. Diese Aufgabe nimmt uns die obige Prozedur jedoch bereits ab: Sie greift auch auf die per Verknüpfung eingebundenen Tabellen zu und untersucht deren Felddatentypen.
Behandlung von Anlage-Feldern
Da wir in Access 2002/2003 ohnehin nicht auf Daten in Anlagefeldern zugreifen können, wäre es am einfachsten, diese Tabelle direkt zu löschen. Allerdings kann es ja sein, dass wir die enthaltenen Bilder noch für Menü- und Symbolleisten oder für Kontextmenüs benötigen. Nun gibt es drei Möglichkeiten:
- Sie verfügen über eine Kopie der Bilddateien im Dateisystem und können diese später wieder in die Access 2002/2003-Version Ihrer Anwendung einbinden,
- Sie speichern alle Bilder dieser Tabelle in das Dateisystem, um diese später wieder einzubinden, oder
- Sie übertragen die Bilder gleich in eine ähnliche aufgebaute Tabelle, welche die Bilder allerdings in einem OLE-Feld speichert.
Die erste Variante erfordert keinen Aufwand. Die zweite können Sie je nach der Anzahl der Bilder manuell durchführen oder Sie verwenden auch hier eine Prozedur, um die Bilder auf die Festplatte zu bannen. Dazu vewenden Sie die Prozedur aus Listing 1. Diese liest zunächst die drei Felder Data.Filedata, Name und Extension aus der Tabelle MSysResources ein.
Listing 1: Dateien aus MSysResources speichern
Public Function DateienSpeichern() Dim rst As DAO.Recordset2 Dim strFilename As String On Error GoTo Fehler Set rst = CurrentDb.OpenRecordset("SELECT Data.Filedata, Name, Extension FROM MSysResources", _ dbOpenSnapshot) Do While Not rst.EOF strFilename = CurrentProject.Path & "\" & rst!Name & "." & rst!Extension If Dir(strFilename) <> "" Then Kill strFilename DoEvents End If On Error Resume Next rst("Data.Filedata").SaveToFile strFilename If Err.Number = (-2146697202) Then rst!Data.SaveToFile strFilename & ".dat" DoEvents Name strFilename & ".dat" As strFilename End If rst.MoveNext Loop Ende: On Error Resume Next Set rst = Nothing Exit Function Fehler: MsgBox Err.Number & "/" & Err.Description, vbCritical Resume Ende End Function
Dabei bezieht sich Data.Filedata direkt auf das Feld Filedata der intern im Anlagefeld Data gespeicherten Untertabelle. Dies verdeutlicht eine Abfrage auf Basis der Tabelle MSysResources in der Entwurfsansicht (s. Bild 5).
Bild 5: Zugriff auf die Felder eines Attachment-Felds
Wir gehen an dieser Stelle davon aus, dass jeder Datensatz der Tabelle MSysResources immer nur mit einer Datei befüllt wurde. Dazu durchläuft die Prozedur alle Datensätze dieser Tabelle in einer Do While-Schleife. Innerhalb dieser Schleife ermittelt die Prozedur zunächst den Dateinamen aus den beiden Feldern Name und Extension.
Die Dateien sollen im Verzeichnis der Datenbank gespeichert werden. Dabei prüft die Prozedur, ob eine Datei gleichen Namens bereits vorhanden ist, und löscht diese gegebenenfalls zuvor.
Dabei kommt die Dir-Funktion zum Einsatz, die eine leere Zeichenkette zurückliefert, wenn die als Parameter angegebene Datei nicht vorhanden ist.
Dann versucht die Prozedur, die im Feld Data.Filedata enthaltene Datei mit der SaveToFile-Methode unter dem in strFilename enthaltenen Dateinamen zu speichern.
Gelingt dies nicht, liegt dies in der Regel daran, dass Access Sicherheitsbedenken wegen des Dateityps hat.
Dies erkennt Access allein an der Dateiendung, weshalb die Prozedur in diesem Fall einfach die erlaubte Dateiendung .dat anhängt, die Datei speichert und dann die Dateiendung wieder entfernt.
Dieser Vorgang wird für jeden Datensatz wiederholt, was eine mehr oder weniger große Menge Dateien in das Verzeichnis der aktuellen Datenbank spült.
Wenn Sie die Dateien in einem anderen Verzeichnis speichern möchten, ändern Sie einfach die Zeile
strFilename = CurrentProject.Path & "\" & rst!Name & "." & rst!Extension
ab, indem Sie hinter "\" ein Unterverzeichnis anhängen – zum Beispiel so:
strFilename = CurrentProject.Path & "\pics\" & rst!Name & "." & rst!Extension
Auf diese Weise werden die Bilder in einem Unterverzeichnis namens pics gespeichert, welches zuvor noch erzeugt werden muss – beispielsweise so (gleich hinter der Zeile Dim strFilename …):
On Error Resume Next MkDir CurrentProject.Path & "\pics\"
Mit On Error Resume Next verhindern Sie, dass ein eventuell vorhandenes Verzeichnis gleichen Namens einen Fehler auslöst.
Vom Anlage-Feld zum OLE-Feld
Wenn Sie die im Anlagefeld gespeicherten Bilder und weitere Dateien anderweitig von der nach Access 2002/2003 migrierten Anwendung aus nutzen möchten und diese in einem Feld einer Tabelle speichern wollen, müssen Sie diese in einem OLE-Feld unterbringen.
Da die Tabelle MSysResources bereits vorhanden ist, können Sie diese gleich weiterverwenden: Dazu müssen Sie der Tabelle lediglich ein neues OLE-Feld etwa namens FileOLE hinzufügen und die Daten für jeden Datensatz aus dem Anlagefeld Data.Filedata in das OLE-Feld übertragen.
Das ist manuell gar nicht zu erledigen, sodass wir hier auf jeden Fall eine entsprechende VBA-Routine benötigen. Diese sieht wie in Listing 2 aus.
Listing 2: Inhalt vom Anlagefeld in ein OLE-Feld übertragen
Public Function Anlage2OLE() Dim db As DAO.Database Dim tdf As DAO.TableDef Dim fld As DAO.Field Dim rst As DAO.Recordset Dim bintmp() As Byte, bintmp2() As Byte Dim lOffset As Long Dim buffer() As Byte Set db = CurrentDb Set tdf = db.TableDefs("MSysResources") On Error Resume Next Set fld = tdf.Fields("FileOLE") If fld Is Nothing Then Set fld = tdf.CreateField("FileOLE", dbLongBinary) tdf.Fields.Append fld End If On Error GoTo 0 Set rst = db.OpenRecordset("SELECT Data.Filedata, FileOle FROM MSysResources", dbOpenDynaset) Do While Not rst.EOF bintmp = rst(0).Value lOffset = bintmp(0) ReDim bintmp2(UBound(bintmp) - lOffset) CopyMemory bintmp2(0), bintmp(lOffset), UBound(bintmp2) buffer = bintmp2 rst.Edit rst!FileOLE.AppendChunk buffer rst.Update rst.MoveNext Loop Erase bintmp Erase bintmp2 Set tdf = Nothing Set db = Nothing End Function
Die Prozedur prüft zunächst, ob die Tabelle bereits ein Feld namens FileOLE enthält – gegebenenfalls führen Sie diese Prozedur ja mehrfach aus, was bei Vorhandensein dieses Feldes einen Fehler bei der Neuanlage auslösen würde. Die Prozedur versucht also, eine Referenz auf das Feld in der Variablen fld zu speichern.
Da dies beim Fehlen des Feldes zu einem Fehler führt, wird die Fehlerbehandlung zuvor mit On Error Resume Next ausgeschaltet. Befindet sich anschließend ein Objektverweis in der Variablen (fld ist nicht Nothing), erstellt die Prozedur mit der Methode Add der Fields-Auflistung des TableDef-Objekts zur Tabelle MSysResources dieses Feld mit dem Typ dbLongBinary, was dem Felddatentyp OLE-Objekt entspricht. Die Tabelle sieht anschließend wie in Bild 6 aus.
Bild 6: MSysResources mit neuem OLE-Feld