Im Beitrag „Access: Version und Architektur einfach ermitteln“ (www.access-im-unternehmen.de/1606) haben wir die neuen SysCmd-Konstanten zum Abfragen von Access-Version und Access-Architektur vorgestellt. Dort haben wir auch gezeigt, wie wir diese Informationen übersichtlich in einem Formular darstellen können. Im vorliegenden Beitrag erstellen wir nun ein COM-Add-In, das den Dateibereich von Access erweitert und dort die verschiedenen Versionsinformationen, die Architektur und den aktuellen Update-Kanal von Access anzeigt. Außerdem liefern wir dort auch gleich noch den aktuellen Datenbankpfad und eine Möglichkeit, diesen in die Zwischenablage zu kopieren.
Die neuen SysCmd-Konstanten wie acSysCmdGetMsoBuildNumber oder acSysCmdGetBitness bieten uns die Möglichkeit, Versionsinformationen oder die Architektur der aktuellen Installation von Microsoft Access per VBA zu ermitteln.
Damit erhalten wir eine Alternative dazu, die Informationen umständlich über das Datei-Menü von Access zu ermitteln. Wenn wir allerdings die Version und die Bitness der Anwendung eines Kunden abfragen wollen, ist es wohl ebenso umständlich, diesem die entsprechende SysCmd-Anweisung zur Eingabe in den Direktbereich des VBA-Editors zu diktieren und uns das Ergebnis mitteilen zu lassen.
Da wir in früheren Beiträgen bereits einige COM-Add-Ins mit dem zumindest für die 32-Bit-Version kostenlosen twinBASIC programmiert haben (das Kompilieren von 64-Bit-COM-Add-Ins ist auch möglich, allerdings wird dort bei jedem Start ein Spash-Screen eingeblendet), wollen wir hier ein weiteres Beispiel dafür liefern, wie man die Benutzerumgebung von Access selbst um praktische Funktionen erweitern kann.
Das Ergebnis sehen wir vorab in Bild 1. Wenn der Benutzer im Ribbon auf Datei klickt, findet er im Datei-Menü direkt einen Eintrag namens amvAccessSpecs. Dahinter zeigt sich die Ansicht mit den Access-Spezifikationen, die wir überwiegend mit den neuen SysCmd-Konstanten ermittelt haben.

Bild 1: Neuer Bereich im Datei-Abschnitt des Ribbons von Microsoft Access
Außerdem haben wir noch den aktuellen Datenbankpfad hinzugefügt, den wir entweder markieren und kopieren oder auch direkt im Windows Explorer öffnen können.
Funktionsweise des COM-Add-Ins
An dieser Stelle wollen wir uns die Beschreibung sparen, wie das vollständige COM-Add-In auf Basis der Vorlage Sample 5 – MyCOMAddIn erstellen – letztlich müssen wir dort nur den anschließend vorgestellten Code ersetzen.
Also empfehlen wir, zum Studium und für eigene Anpassungen einfach das mit dem Download gelieferte twinBASIC-Projekt namens amvAccessSpecs.twinproj zu nutzen.
Die Auflistung der Versionen von twinBASIC finden Sie unter dem folgenden Link:
https://github.com/twinbasic/twinbasic/releases
Hier klicken wir auf die aktuellste Version und laden diese wie in Bild 2 herunter – hier die Version twinBASIC BETA 983.

Bild 2: Herunterladen der aktuellen twinBASIC-Version
Und keine Sorge wegen des Beta-Status – die damit erzeugten COM-Add-Ins funktionieren einwandfrei.
Projekteigenschaften
In den Projekteigenschaften haben wir Projekt Name, Project Description und Application Title jeweils auf amvAccessSpecs eingestellt.
Außerdem benötigen wir Verweise auf die Bibliotheken Microsoft Office 16.0 Object Library und Microsoft Access 16.0 Object Library.
Code des COM-Add-Ins
Der Code, der das COM-Add-In beim Starten von Access lädt und für die Anzeige des neuen Bereichs im Backstage sorgt, befindet sich im Modul amvAccessSpecs.twin.
Diesen öffnen wir durch einen Doppelklick auf den entsprechenden Eintrag im Projektexplorer (siehe Bild 3).

Bild 3: Überblick des Projekts und das Modul amvAccessSpecs.twin
Wir schauen uns den Code nun Schritt für Schritt an.
Die Klasse und ihre Schnittstellen
Im oberen Bereich des Moduls finden wir die Deklaration der Klasse amvAccessSpecs samt den beiden Schnittstellen, die sie umsetzt.
Ganz oben sehen wir die ClassId:
[ClassId("319E65DE-8BB5-4F-890C-598D98FCCC22")]
Die ClassId ist die eindeutige Identität der COM-Klasse – ein weltweit einmaliger Schlüssel (eine GUID), unter dem Windows das COM-Add-In in der Registry findet und startet.
Der Hintergrund: COM-Objekte werden nicht über ihren Namen angesprochen, sondern über diese GUID (CLSID genannt). Wenn Access das Add-In lädt, schaut es in der Registry unter genau dieser Nummer nach, wo die zugehörige Datei liegt und welche Klasse zu instanziieren ist. Der Klassenname amvAccessSpecs ist nur für uns als Entwickler da; Windows arbeitet intern mit der GUID.
Deshalb ist zweierlei wichtig: Die Nummer muss wirklich einmalig sein (twinBASIC erzeugt sie zufällig, die Wahrscheinlichkeit einer Kollision ist praktisch null), und sie sollte stabil bleiben. Ändern wir sie nachträglich, gilt das Add-In für Windows als ein völlig anderes Objekt – die alte Registrierung zeigt dann ins Leere, und wir müssten neu registrieren. Beim Weitergeben oder Aktualisieren des Add-Ins lassen wir die ClassId also unverändert.
Kurz gesagt: Sie ist der Personalausweis der Klasse, über den COM sie zweifelsfrei wiederfindet.
Anschließend folgt die Definition der Klasse amvAccessSpecs selbst mit den beiden Schnittstellen, die sie implementiert:
Public Class amvAccessSpecs Implements IDTExtensibility2 [WithDispatchForwarding] Implements IRibbonExtensibility Friend objAccess As Access.application
Eine Schnittstelle ist so etwas wie ein Versprechen: Wer sie umsetzt, sagt zu, bestimmte Prozeduren bereitzustellen. IDTExtensibility2 sorgt dafür, dass Office unser Add-In starten und beenden kann. IRibbonExtensibility erlaubt es uns, dem Menüband – genauer dem Datei-Menü – etwas hinzuzufügen.
Die Zeile Friend objAccess As Access.application legt außerdem eine Variable an, in der wir gleich einen Verweis auf Access selbst ablegen. Über diese stellen wir Access später unsere Fragen nach Version und Architektur. Die Angaben in eckigen Klammern sind technische Markierungen, die twinBASIC für ein COM-Add-In benötigt; um sie müssen wir uns nicht kümmern.
Wenn Access das Add-In lädt
Sobald Access startet und unser Add-In verbindet, ruft es die Prozedur OnConnection auf. Genau hier merken wir uns den Verweis auf Access (siehe Listing 1).
Sub OnConnection(ByVal Application As Object, ByVal ConnectMode As ext_ConnectMode, ByVal AddInInst As Object, _ ByRef custom As Variant()) Implements IDTExtensibility2.OnConnection Set objAccess = Application End Sub
Listing 1: Beim Verbinden den Verweis auf Access merken
Set objAccess = Application speichert einen Verweis auf die übergebene Anwendung in unserer Variablen. Die Schnittstelle IDTExtensibility2 verlangt noch vier weitere Prozeduren – OnDisconnection, OnAddInsUpdate, OnStartupComplete und OnBeginShutdown -, die wir hier aber leer lassen, weil wir sie nicht benötigen. Sie müssen aber angelegt sein.
Die Oberfläche im Datei-Menü
Wie unser Bereich im Datei-Menü aussieht, beschreiben wir als XML-Definition. Die Funktion GetCustomUI baut diesen Text Zeile für Zeile zusammen und gibt ihn an Access zurück (siehe Listing 2).
Private Function GetCustomUI(ByVal RibbonID As String) As String Implements IRibbonExtensibility.GetCustomUI Dim strXML As String strXML = "<customUI xmlns=""http://schemas.microsoft.com/office/2009/07/customui"" onLoad=""customUI_OnLoad"">" & vbCrLf strXML = strXML & " <backstage>" & vbCrLf strXML = strXML & " <tab id=""tabSQLServer"" label=""amvAccessSpecs"" insertAfterMso=""TabInfo"" " _ & "title=""Access-Spezifikationen"">" & vbCrLf strXML = strXML & " <firstColumn>" & vbCrLf strXML = strXML & " <group id=""grpVersionsUndArchitektur"" label=""Version und Architektur"">" & vbCrLf strXML = strXML & " <topItems>" & vbCrLf strXML = strXML & " <layoutContainer id=""lcTreiber"" layoutChildren=""vertical"">" & vbCrLf strXML = strXML & " <editBox id=""ebFullVersion"" label=""Volle Version"" " _ & "sizeString=""WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"" getText=""getText""/>" & vbCrLf strXML = strXML & " <editBox id=""ebVersion"" label=""Version"" " _ & "sizeString=""WWWWWWWWWWWWWWWWWWWW"" getText=""getText""/>" & vbCrLf strXML = strXML & " <editBox id=""ebAccessVer"" label=""Access-Version"" " _ & "sizeString=""WWWWWWWWWWWWWWWWWWWW"" getText=""getText""/>" & vbCrLf strXML = strXML & " <editBox id=""ebMsoBuildNumber"" label=""MSO-Buildnummer"" " _ & "sizeString=""WWWWWWWWWWWWWWWWWWWW"" getText=""getText""/>" & vbCrLf strXML = strXML & " <editBox id=""ebFullBuildNumber"" label=""Volle Buildnummer"" " _ & "sizeString=""WWWWWWWWWWWWWWWWWWWW"" getText=""getText""/>" & vbCrLf strXML = strXML & " <editBox id=""ebChannelName"" label=""Channel-Name"" " _ & "sizeString=""WWWWWWWWWWWWWWWWWWWW"" getText=""getText""/>" & vbCrLf strXML = strXML & " <editBox id=""ebBitness"" label=""Architektur"" " _ & "sizeString=""WWWWWWWWWWWWWWWWWWWW"" getText=""getText""/>" & vbCrLf strXML = strXML & " <editBox id=""ebBuildNumber"" label=""Buildnummer"" " _ & "sizeString=""WWWWWWWWWWWWWWWWWWWW"" getText=""getText""/>" & vbCrLf strXML = strXML & " </layoutContainer>" & vbCrLf strXML = strXML & " </topItems>" & vbCrLf strXML = strXML & " </group>" & vbCrLf strXML = strXML & " <group id=""grpWeitereInformationen"" label=""Weitere Informationen"">" & vbCrLf strXML = strXML & " <topItems>" & vbCrLf strXML = strXML & " <layoutContainer id=""lcWeitereInformationen"" layoutChildren=""vertical"">" & vbCrLf strXML = strXML & " <layoutContainer id=""lcDatabaseFolder"" layoutChildren=""horizontal"">" & vbCrLf strXML = strXML & " <editBox id=""ebDatabaseFolder"" label=""Datenbankorder"" " _ & "sizeString=""WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"" getText=""getText""/>" & vbCrLf strXML = strXML & " <button id=""btnOpenDatabaseFolder"" label=""Öffnen"" " _ & "imageMso=""FileOpen"" onAction=""btnOpenDatabaseFolder_onAction""/>" & vbCrLf strXML = strXML & " </layoutContainer>" & vbCrLf strXML = strXML & " </layoutContainer>" & vbCrLf strXML = strXML & " </topItems>" & vbCrLf strXML = strXML & " </group>" & vbCrLf strXML = strXML & " </firstColumn>" & vbCrLf strXML = strXML & " </tab>" & vbCrLf strXML = strXML & " </backstage>" & vbCrLf strXML = strXML & "</customUI>" & vbCrLf Return strXML End Function
Listing 2: Die Funktion GetCustomUI stellt die Oberfläche wird als XML zusammen
Das hat einen Vorteil: Wir bestimmen damit ganz genau, welche Felder und Schaltflächen erscheinen und wie sie angeordnet sind. Die Funktion GetCustomUI baut diese Beschreibung Zeile für Zeile zusammen und gibt sie an Access zurück.
Ganz außen steht das Element customUI. Es ist die Klammer um alles Weitere und nennt mit onLoad die Prozedur, die Access einmal aufrufen soll, sobald die Oberfläche geladen ist.
Darin folgt backstage – und das ist genau der Bereich, der erscheint, wenn der Benutzer links oben auf Datei klickt, nicht eine der gewöhnlichen Registerkarten des Menübands.
Im backstage legen wir mit tab unsere eigene Seite an. Sie trägt die Beschriftung amvAccessSpecs, und mit der Angabe insertAfterMso samt dem Wert TabInfo schieben wir sie genau hinter den bereits vorhandenen Bereich Informationen. So fügt sich unser Eintrag nahtlos in das gewohnte Datei-Menü ein.
Innerhalb dieser Seite ordnen wir den Inhalt in zwei Gruppen: Version und Architektur und Weitere Informationen. Eine Gruppe ist dabei nichts weiter als ein zusammengehöriger Block mit einer Überschrift. Die eigentliche Anordnung der Felder übernimmt in jeder Gruppe ein layoutContainer.
Über dessen Angabe legen wir fest, ob die enthaltenen Elemente untereinander (vertical) oder nebeneinander (horizontal) stehen. Die vielen Versionsfelder stapeln wir untereinander; das Pfadfeld und die Öffnen-Schaltfläche dagegen setzen wir nebeneinander.
Die einzelnen Anzeigefelder sind Elemente vom Typ editBox. Jedes bekommt eine eigene Kennung (id), eine Beschriftung (label) und über sizeString eine feste Breite – dafür geben wir schlicht eine Kette von W-Buchstaben an, die so breit ist, wie das Feld später sein soll.
Und jedes Feld nennt mit getText die Funktion, die seinen Inhalt liefert.
In der zweiten Gruppe kommt neben dem Pfadfeld noch eine Schaltfläche hinzu, das Element button. Sie zeigt über imageMso ein vorhandenes Office-Symbol – hier FileOpen, das Symbol zum Öffnen – und ruft über onAction beim Anklicken unsere Prozedur btnOpenDatabaseFolder_onAction auf.
Der eigentliche Kniff an dieser Beschreibung: Sie sagt nur, welche Felder es gibt und wie sie aussehen – nicht, was darin steht. Den Inhalt holt sich jedes Feld selbst, indem es über seine id die Funktion getText befragt; ebenso meldet sich die Schaltfläche über onAction, sobald sie geklickt wird. So bleiben Aussehen und Inhalt sauber getrennt: Die XML-Definition kümmert sich um die Optik, der VBA-Code um die Werte.
Der Ribbon meldet sich
Wenn Office unsere Oberfläche geladen hat, ruft es einmal die Funktion customUI_OnLoad auf. Darin merken wir uns das Menüband in der Variablen m_ribbon:
Unser exklusives Angebot für Dich!
(Gilt für den Abschluss eines Jahres-Abonnements im ersten Jahr, danach 189,-/Jahr)
Hier geht’s weiter →Die ersten 4 Wochen kostenlos testen – voller Zugriff auf alle Artikel, vollständigen Code und Beispieldatenbanken. Kein Risiko: Wenn es nicht passt, kündigst Du einfach innerhalb der ersten vier Wochen.
Hast Du eine konkrete Frage zu Deiner eigenen Access-Anwendung?
Vielleicht stellt Deine Anwendung Dich vor eine Herausforderung, zu der Du bisher keine Lösung findest. Schlechte Performance, kein ausreichender Zugriffsschutz, Du bist unsicher über Dein Datenmodell oder Dein Code liefert unerklärliche Fehler?
In unserem kostenlosen Access-Audit schaut sich André Minhorst persönlich gemeinsam mit Dir Deine Lösung per Zoom an – und zeigt Dir, wo Datenmodell, VBA-Code, Ergonomie und Sicherheit Optimierungspotenzial bieten.
Jetzt kostenloses Access-Audit anfordern →