Add in and find out

15.08.2006 von Lorenz  Hölscher
Bei jeder Access-Datenbank kommt irgendwann einmal ein Analysetool zum Einsatz. Doch es ist kaum praktikabel, ein Analysetool mit seinen Tabellen, Abfragen, Formularen, Berichten und Modulen jedes Mal in die zu untersuchende Datenbank hineinzukopieren. Ein Add-In hilft hier weiter.

Neben dem Problem, dass nicht zwei Datenbanken gleichzeitig sichtbar geöffnet sein können, müssen Sie beim Einsatz eines Analysetools darauf achten, dass nicht die mitgebrachten Analyseobjekte versehentlich auch analysiert werden – ganz abgesehen davon, dass gleichnamige Objekte beim Kopieren leicht überschrieben oder entscheidende Daten wie die Datenbankgröße verändert werden.

Zwei Datenbanken gleichzeitig

Sie müssen also von außen in die Datenbank hineinsehen. Und dafür ist ein Add-In optimal, denn der Benutzer kann jederzeit auf dessen Fähigkeiten zugreifen, während seine eigene Datenbank geöffnet ist.

Dabei unterscheidet sich eine Add-In-Datenbank erst einmal gar nicht von anderen Datenbanken. Es wird genauso ein Startformular vorhanden sein, welches alle Aufgaben erledigt oder weitere Formulare (oder Berichte) öffnet.

Mit der Beispieldatenbank AddIn.mdb soll die Größe der gesamten Datenbank, die Anzahl der Datensätze aller Tabellen und Abfragen sowie die Anzahl der Controls auf allen Formularen ermittelt werden. Das zeigt mit geringem technischem Aufwand alles Notwendige.

Erstellen Sie in AddIn.mdb eine neue Tabelle tblAnalyse mit dem AutoWert-Feld AID sowie den drei Textfeldern AName, AInhalt und AWert. Hierin werden die Analysedaten zwischengespeichert und anschließend vom Formular angezeigt.

Außerdem benötigen Sie noch ein leeres Formular frmStart ohne Datenbindung mit einem Label lblAnalyse und einer Listbox lstAnalyse. Die Listbox wird auf den Herkunftstyp:Wertliste und die Spaltenanzahl: 3 gestellt.

Die eigentliche Analyse findet beim EreignisForm_Load wie in Listing 1 statt.

Option Compare Database
Option Explicit
Private Sub Form_Load()
Dim dbsAddIn As Database
Dim rcsAddIn As Recordset
Dim dbsAnalyse As Database
Dim prjAnalyse As CurrentProject
Dim rcsAnalyse As Recordset
Dim frmAnalyse As Form
Dim strName As String
Dim intAnzahl As Integer
Dim i As Integer
Set dbsAddIn = CodeDb
Set rcsAddIn = dbsAddIn.OpenRecordset("tblAnalyse")
Set dbsAnalyse = CurrentDb
Set prjAnalyse = CurrentProject
On Error Resume Next
'vorherige Analyse-Daten löschen
dbsAddIn.Execute "DELETE * FROM tblAnalyse"
'Name der Datenbank ermitteln
Schreibe rcsAddIn, "Datenbank " & String(50, "_"), _
dbsAnalyse.Name, "dbs"
'Größe der Datenbank ermitteln
Schreibe rcsAddIn, "Größe ", _
Format(FileLen(dbsAnalyse.Name) / 1024, "#,## kB"), "dbs"
'Überschrift für die Tabellen
Schreibe rcsAddIn, "", "", ""
Schreibe rcsAddIn, dbsAnalyse.TableDefs.Count & _
" Tabellen " & String(50, "_"), String(50, "_"), "tbl"
'Anzahl Datensätze in Tabellen
intAnzahl = 0
For i = 0 To dbsAnalyse.TableDefs.Count - 1
strName = dbsAnalyse.TableDefs(i).Name
Set rcsAnalyse = dbsAnalyse.OpenRecordset(strName)
Schreibe rcsAddIn, strName, rcsAnalyse.RecordCount & _
" Datensätze", "tbl"
intAnzahl = intAnzahl + 1
Next
'Überchrift für die Abfragen
Schreibe rcsAddIn, "", "", ""
Schreibe rcsAddIn, dbsAnalyse.QueryDefs.Count & _
" Abfragen " & String(50, "_"), String(50, "_"), "qry"
'Anzahl Datensätze in Abfragen
For i = 0 To dbsAnalyse.QueryDefs.Count - 1
strName = dbsAnalyse.QueryDefs(i).Name
Set rcsAnalyse = dbsAnalyse.OpenRecordset(strName)
'Abfragen müssen wenigstens einmal bis zum Ende gehen:
If Not rcsAnalyse.EOF Then rcsAnalyse.MoveLast
Schreibe rcsAddIn, strName, rcsAnalyse.RecordCount & _
" Datensätze", "qry"
intAnzahl = intAnzahl + 1
Next
'Überschrift für Formulare
Schreibe rcsAddIn, "", "", ""
Schreibe rcsAddIn, prjAnalyse.AllForms.Count & " Formulare " & _
String(50, "_"), String(50, "_"), "frm"
'Anzahl Kontroll-Elemente in Formularen
For i = 0 To prjAnalyse.AllForms.Count - 1
strName = prjAnalyse.AllForms(i).Name
'nicht sich selber in Entwurf umschalten!
If strName <> "frmStart" Then
DoCmd.OpenForm strName, acDesign
Set frmAnalyse = Forms(strName)
Schreibe rcsAddIn, strName, frmAnalyse.Controls.Count & _
" Kontroll-Elemente", "frm"
intAnzahl = intAnzahl + 1
DoCmd.Close acForm, strName
Else
Schreibe rcsAddIn, strName, "<nicht ermittelbar>", "frm"
intAnzahl = intAnzahl + 1
End If
Next
'Listbox aktualisieren
Me.lstInhalte.Requery
Me.lblInhalte.Caption = intAnzahl & " Analysen"
End Sub
Private Sub Schreibe(RS As Recordset, strName As String, _
strInhalt As String, strTyp As String)
With RS
.AddNew
.Fields("AName").Value = strName
.Fields("AInhalt").Value = strInhalt
.Fields("ATyp").Value = strTyp
.Update
End With
End Sub

Datenbanken unterscheiden

Da der Code zukünftig in der anderen Datenbank lesen, aber in seine eigene Tabelle schreiben soll, müssen Sie mit den Objekt-Variablen dbsAddIn und dbsAnalyse schon jetzt zwischen beiden unterscheiden.

CurrentDB ist immer die sichtbare, vom Benutzer geöffnete Datenbank. Während der Entwicklung ist das auch die Add-In-Datenbank selber. Aber als installiertes Add-In meint es die zu untersuchende Datenbank. Um auf das Add-In selber zugreifen zu können, gibt es das Objekt CodeDB. Zur Vereinfachung beim Eintragen der gefundenen Daten wird es in die Prozedur Schreibe ausgelagert. Diese erzeugt jeweils einen neuen Eintrag in tblAnalyse, darunter auch Leerzeilen für die spätere, übersichtlichere Anzeige in der lstAnalyse. In intAnzahl zählt die Prozedur jeden Analyseschritt mit, damit Überschriften und Leerzeilen ignoriert werden können.

Bevor die Analyse beginnt, wird mit Execute "DELETE..." die Tabelle tblAnalyse geleert. Als erste Information werden Name und Größe der untersuchten Datenbank hinzugefügt. Die Anweisung String(50, "_") hebt mit den 50 Unterstrichen lediglich die Überschrift hervor.

Dann durchläuft der Code ganz herkömmlich die Tabledefs- beziehungsweise die Querydefs-Auflistung, um die Namen aller Tabellen oder Abfragen zu ermitteln. Deren RecordCount-Eigenschaft gibt die Anzahl der Datensätze an, bei Abfragen allerdings erst nach Move-Last.

Einige Auflistungen fehlen

Bei den Formularen gibt es bis Access 97 keine entsprechende Auflistung zu TableDefs oder QueryDefs, erst ab Access2000 liefert das CurrentProject- Objekt mit seiner AllForms-Auflistung die gewünschten Namen aller Formulare. Bis Access 97 müssen Sie etwas mühsamer die versteckte MSysOb jects-Tabelle auslesen, was hier thematisch zu weit führen würde.

Die Forms-Auflistung würde Ihnen hier übrigens nicht weiterhelfen, denn sie zeigt ja nur die geöffneten Formulare an, statt aller.

Da die Formulare schneller und – weil ohne Codeausführung – sicherer im Entwurfsmodus geöffnet werden, muss der Laufzeitfehler abgefangen werden, wenn frmStart selbst analysiert werden soll. Schließlich kann das Programm während seiner eigenen Ausführung nicht in den Entwurfsmodus umschalten.

Sobald diese Datenbank als Add-In aufgerufen wird, befindet sich frmStart ohnehin nicht mehr in der aktuellen Datenbank und deshalb auch nicht in CurrentProject.AllForms, aber während der Entwicklungszeit schon noch.

Wenn Sie es noch um die Schaltflächen btn- Schliessen und btnInfo mit dem Code aus Listing 2 erweitern, ist das Formular vorerst komplett. Das zweite Formular frmInfo fehlt zwar noch, aber das können Sie später nachreichen.

Private Sub btnSchliessen_Click()
DoCmd.Close acForm, Me.Name
End Sub
Private Sub btnInfo_Click()
DoCmd.OpenForm "frmInfo", , , , , acDialog
End Sub

Dieses Formular können Sie nun direkt ausprobieren, es sollte so aussehen wie in Bild 1.

Bild 1: Das Analyseformular.

Jetzt brauchen Sie für das zukünftige Add-In nur noch eine VBA-Funktion aus Listing 3, um frmStart anzuzeigen. Bei Add-Ins kann an den Untermenü-Eintrag nämlich nur eine VBA-Funktion gebunden werden.

Function Start_Prozedur()
DoCmd.OpenForm "frmStart", acNormal, , , , acDialog
End Function

Installation vorbereiten

Die eigentliche Installation des Add-Ins übernimmt später Access selber mit Extras/AddIns/ AddIn-Manager. Dieser Dialog erwartet in der AddIn.mdb eine Tabelle USysRegInfo mit bestimmten Einträgen (Tabelle 1).

Tabelle 1: Inhalte von UsysRegInfo.

Subkey

Type

ValName

Value

HKEY_CURRENT_ACCESS_PROFILE\Menu Add-ins\
Experts inside: &Demo-Addin...

0

HKEY_CURRENT_ACCESS_PROFILE\Menu Add-ins\
Experts inside: &Demo-Addin...

1

Expression

=Start_Prozedur()

HKEY_CURRENT_ACCESS_PROFILE\Menu Add-ins\
Experts inside: &Demo-Addin...

1

Library

|ACCDIR\AddIn.mdb

Die Tabelle USysRegInfo ist übrigens wegen des Präfixes USys automatisch ein System-Objekt und wird deshalb ausgeblendet. Sie können sie mit Extras/Optionen/Ansicht Systemobjekte wieder sichtbar machen, wie auch alle anderen MSys-Tabellen.

In der Tabelle legen Sie in der ersten Spalte Subkey den Registry-Zweig fest. Wirklich variabel ist dabei nur der letzte Teil, welcher den eigentlichen Untermenütitel (hier: Experts inside: Demo- Addin) enthält.Windows-üblich wird der zu unterstreichende Buchstabe für den Shortcut mit einem & davor gekennzeichnet, wie Sie in Bild 2 sehen können.

Bild 2: AddIn-Menü nach der Installation.

Der HKEY_CURRENT_ACCESS_PROFILE-Wert in Subkey wird bei der Installation durch den tatsächlichen Schlüssel (je nach Version beispielsweise HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ Office.0\Access) ersetzt.

Im zweiten Datensatz steht hinter Expression die auszuführende Funktion beliebigen Namens und im dritten hinter Library der Pfad und Name der Datenbank. Dabei wird |ACCDIR während der Installation durch den konkreten Pfad auf dem Zielrechner ersetzt.

Wenn Sie mit dem Add-In fertig sind, werden Sie sicherlich eher eine .mde-Datei statt .mdb erzeugen und ausliefern. Dann muss dort auch die passende Dateiendung eingetragen sein. Jetzt während der Entwicklung wäre eine solchermaßen geschützte Datenbank aber überaus lästig. Wie Sie an der Konstruktion der USysRegInfo- Tabelle sehen können, könnte ein Add-In auch mehrere Menüeinträge installieren. Anstatt das Untermenü damit zu überfrachten, sind aber Buttons in frmStart sicherlich besser. Mit dieser Tabelle ist Ihr Add-In fertig vorbereitet.

Installation durch den Benutzer

Jetzt kommt der zweite Teil, nämlich eine Installation durch den Benutzer. Um das zu testen, öffnen Sie eine beliebige andere Datenbank, denn sonst ist das benötigte Untermenü nicht aktiv. Dann erhalten Sie mit Extras/AddIns/AddIn-Manager den Dialog aus Bild 3.

Bild 3: AddIn- Manager von Access.

Wählen Sie nun mit Hinzufügen Ihr Add-In aus. Das war's schon. Die im Dialog sichtbaren Firmen- und Kommentareinträge entstammen den entsprechenden Datenbankeigenschaften in AddIn.mdb.

Access hat jetzt die Registry-Einträge anhand Ihrer USysRegInfo-Tabelle geschrieben und die AddIn.mdb in das Access-Add-In-Verzeichnis kopiert. Dieses befindet sich typischerweise in C:\Dokumente und Einstellungen\<Ihr Login>\ Anwendungsdaten\Microsoft\AddIns\.

Bild 4: Startformular mit Analysedaten.

Sie finden anschließend das neue Menü Extras/AddIns/Experts inside:Demo-AddIn... wie in Bild 2 und können direkt die Analyse der aktuellen Datenbank ausführen lassen. Das Ergebnis sieht dann ähnlich aus wie in Bild 4.

Änderungen an einem Add-In

Wenn Sie jetzt für das Formular frmInfo noch Änderungen an AddIn.mdb vornehmen wollen, müssen Sie vor einer Bearbeitung des Add-Ins erst Access komplett beenden, denn so genannte Bibliotheksdatenbanken wie dieses Add-In sind gesperrt, sobald sie geladen wurden.

Nach dem Neuladen von AddIn.mdb soll nun noch das andere, ebenfalls ungebundene Formular frmInfo wie in Bild 5 ergänzt werden. Es dient der Anzeige der Pfade und Dateinamen beider Datenbanken.

Bild 5: Entwurf für frmInfo.

Auf dem Entwurf ist außer dem Button btn-Schliessen ein Label lblInfo enthalten, welches erst in Form_Load mit Inhalt gefüllt wird, wie Sie in Listing 4 sehen.

Listing 4: Pfad und Dateinamen beider Datenbanken
Private Sub btnSchliessen_Click()
DoCmd.Close
End Sub
Private Sub Form_Load()
Me.lblInfo.Caption = "Autor: Lorenz Hölscher" & vbCrLf & _
"Addin-DB: " & LCase(CodeDb.Name) & vbCrLf & _
"Analysierte DB: " & LCase(CurrentDb.Name)
End Sub

Hierbei sieht man nun, wie in Bild 6 dargestellt, deutlich den Unterschied zwischen Code-DB und CurrentDB bei einem Add-In.

Bild 6: Info-Formular in einer Testdatenbank.

Damit ist Ihr Add-In fertig und kann auf wesentliche Teile der Datenbank zugreifen, die ein Benutzer analysieren möchte.