Suchen und Ersetzen mit COM-Add-In

Im Beitrag „Besserer Suchen und Ersetzen-Dialog“ haben wir den Dialog zum Suchen und Ersetzen in Access-Formularen nachgebaut und um einige Features ergänzt. Nun wollen wir unseren selbst gebauten Dialog noch für jede Access-Anwendung verfügbar machen. Was liegt da näher, als diesen über das Ribbon aufzurufen? Um eine solche Erweiterung für jede geöffnete Anwendung verfügbar zu machen und so flexibel wie möglich zu sein, verwenden wir dazu ein COM-Add-In. Dieses enthält lediglich die Erweiterung des Ribbons um die benötigten Befehlsschaltflächen sowie den Code, um von dort aus den „Suchen und Ersetzen“-Dialog zu starten.

COM-Add-Ins erstellen mit twinBASIC

Unsere erste Wahl für das komfortable Erstellen von COM-Add-Ins für Microsoft Access ist twinBASIC. Diese Entwicklungsumgebung ist in der 32-Bit-Version kostenlos und 64-Bit-DLLs können ebenfalls damit erstellt werden – in der kostenlosen Version erscheint hier allerdings ein Splash-Screen des Herstellers.

Den jeweils aktuellen Release kann man hier herunterladen:

https://github.com/twinbasic/twinbasic/releases

Hier klickt man auf den gewünschten Release und lädt beispielsweise für den Release mit der Nummer 784 die Datei twinBASIC_IDE_BETA_784.zip herunter.

Den Inhalt entpackt man in ein neues, leeres Verzeichnis. Es ist keine weitere Installation notwendig, wir brauchen einfach nur die Datei twinBASIC.exe zu starten. Nach dem Start erscheint ein Startdialog, in dem wir zur Registerseite Samples wechseln. Hier finden wir den Eintrag Sample 5. MYCOMAddIn, den wir doppelt anklicken (siehe Bild 1).

Anlegen eines COM-Add-In-Projekts mit Beispiel

Bild 1: Anlegen eines COM-Add-In-Projekts mit Beispiel

Danach erscheint ein weiterer Dialog namens twinBASIC New Project Options. Hier passen wir den Projektnamen an, in diesem Fall auf amvSuchenUndErsetzen (siehe Bild 2).

Angeben des Projektnamens

Bild 2: Angeben des Projektnamens

twinBASIC erstellt nun in wenigen Augenblicken das neue Projekt und öffnet das Modul MyCOMAddin.twin.

Verweise hinzufügen

Da wir ein COM-Add-In erstellen wollen, fügen wir diesem zunächst einen Verweis auf die Objektbibliothek von Microsoft Access hinzu.

Dazu öffnen wir den References-Bereich mit dem Menübefehl Project|References… (siehe Bild 3).

Öffnen der Referenzen

Bild 3: Öffnen der Referenzen

Dort angekommen, wechseln wir zur Registerseite Available COM References und geben im Suchbereich microsoft access ein. Dies bringt den Verweis auf die Bibliothek Microsoft Access 16.0 Object Library zum Vorschein, die wir nun aktivieren (siehe Bild 4). Anschließend klicken wir auf Save Changes.

Hinzufügen eines Verweises auf die Access-Bibliothek

Bild 4: Hinzufügen eines Verweises auf die Access-Bibliothek

Einstellungen anpassen

Danach öffnen wir mit Project|Project Settings weitere Einstellungen, wo wir jeweils den Namen unseres Projekts eingeben können (siehe Bild 5).

Einstellen von Name, Description und Application Title

Bild 5: Einstellen von Name, Description und Application Title

.twin-Datei umbenennen

Nun klicken wir mit der rechten Maustaste links auf den Eintrag Sources|MyCOMAddin.twin und wählen aus dem Kontextmenü den Befehl Rename aus (siehe Bild 6). Wir können den Namen von MyCOMAddin.twin nun bearbeiten und ändern diesen auf SuchenUndErsetzen.twin.

Umbenennen der Code-Datei

Bild 6: Umbenennen der Code-Datei

Achtung: Die Datei muss zu diesem Zweck geschlossen sein.

Prozeduren zum Registrieren des COM-Add-Ins

Wie sehen neben der frisch umbenannten Datei SuchenUndErsetzen.twin noch eine weitere Datei namens DllRegistration.twin. Wenn wir diese öffnen, sehen wir verschiedene Elemente, die wir durch den Inhalt aus Listing 1 ersetzen.

Module DllRegistration
     Const AddinProjectName As String = VBA.Compilation.CurrentProjectName
     Const AddinClassName As String = "SuchenUndErsetzen"
     Const AddinQualifiedClassName As String = AddinProjectName & "." & AddinClassName
     Const RootRegistryFolder As String = "HKCU\SOFTWARE\Microsoft\Office\Access\Addins\" & AddinQualifiedClassName & "\"
     Public Function DllRegisterServer() As Boolean
         On Error GoTo RegError
         Dim wscript As Object = CreateObject("wscript.shell")
         wscript.RegWrite RootRegistryFolder & "FriendlyName", AddinProjectName, "REG_SZ"
         wscript.RegWrite RootRegistryFolder & "Description", AddinProjectName, "REG_SZ"
         wscript.RegWrite RootRegistryFolder & "LoadBehavior", 3, "REG_DWORD"
         Return True
     RegError:
         MsgBox "DllRegisterServer -- An error occured trying to write to the system registry:" & vbCrLf & _
                 Err.Description & " (" & Hex(Err.Number) & ")"
         Return False
     End Function
     Public Function DllUnregisterServer() As Boolean
         On Error GoTo RegError
         Dim wscript As Object = CreateObject("wscript.shell")
         wscript.RegDelete RootRegistryFolder & "FriendlyName"
         wscript.RegDelete RootRegistryFolder & "Description"
         wscript.RegDelete RootRegistryFolder & "LoadBehavior"
         wscript.RegDelete RootRegistryFolder
         Return True
     RegError:
         MsgBox "DllUnregisterServer -- An error occured trying to delete from the system registry:" & vbCrLf & _
                 Err.Description & " (" & Hex(Err.Number) & ")"
         Return False
     End Function
End Module

Listing 1: Prozeduren zum Registrieren des COM-Add-Ins

Hier sehen wir verschiedene Elemente. Ganz oben finden wir einige Konstanten, die unter anderem mit dem Projektnamen gefüllt werden (AddinProjectName) oder mit dem Namen der zu verwendenden Klasse (SuchenUndErsetzen).

Darunter finden wir zwei Funktionen. Diese Funktionen werden ausgeführt, wenn wir das COM-Add-In registrieren oder die Registrierung aufheben wollen, zum Beispiel über die Kommandozeile.

Die erste Funktion DllRegisterServer nimmt die für den Einsatz notwendigen Einträge in der Registry vor, die zweite Funktion DllUnregisterServer entfernt diese wieder.

Code der COM-DLL

Nun müssen wir nur noch den Code erstellen, mit dem wir die gewünschten Einträge zum Ribbon hinzufügen und dafür sorgen, dass das COM-Add-In überhaupt ausgeführt wird, wenn wir Access starten.

Dazu benötigen wir die Schnittstelle IDTExtensibility, die wir durch Hinzufügen mit dem Schlüsselwort Implements zur Klasse SuchenUndErsetzen bekannt geben.

Außerdem benötigen wir noch eine zweite Schnittstelle, damit wir über eine Funktion die Ribbondefinition übergeben können. Diese machen wir durch Anhängen von Implements IRibbonExtensibility und dem zusätzlichen Hinweis [WithDispathForwarding] für die Klasse bekannt.

IDTExensibility2-Schnittstelle implementieren

Durch das Implementieren diese Schnittstellen müssen wir die dadurch definierten Methoden, Ereignisse und Eigenschaften ebenfalls im Code implementieren. Bei der Schnittstelle IDTExtensibility2 bedeutet dies, dass wir einige Ereignisprozeduren anlegen müssen, die beim Laden des COM-Add-Ins durch Access ausgelöst werden, andere werden zu anderen Zeitpunkten aktiviert. Die wichtigste für uns ist die Ereignisprozedur OnConnection, die beim Start von Access ausgelöst wird und mit der Access uns mit dem Parameter Application einen Verweis auf die aktuelle Access-Instanz übergibt. Diesen Verweis speichern wir in der Variablen objAccess, damit wir später darüber auf die Access-Instanz zugreifen können. Das ist in diesem Fall streng genommen gar nicht nötig, aber wir führen diesen Code immer mit, weil er für die meisten COM-Add-Ins, die direkt Aktionen mit dem Access-Objekt ausführen sollen, nötig ist.

Diesen Teil des Codes finden Sie in Listing 2.

[ClassId("DC8E7154-FAC8-43B7-88E5-A93527539222")]
Class SuchenUndErsetzen
     Implements IDTExtensibility2
     [WithDispatchForwarding]
     Implements IRibbonExtensibility
     Private objAccess As Access.Application
     Private objRibbon As IRibbonUI
     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
     Sub OnDisconnection(ByVal RemoveMode As ext_DisconnectMode, ByRef custom As Variant()) _
             Implements IDTExtensibility2.OnDisconnection
     End Sub
     Sub OnAddInsUpdate(ByRef custom As Variant()) Implements IDTExtensibility2.OnAddInsUpdate
     End Sub
     Sub OnStartupComplete(ByRef custom As Variant()) Implements IDTExtensibility2.OnStartupComplete
     End Sub
     Sub OnBeginShutdown(ByRef custom As Variant()) Implements IDTExtensibility2.OnBeginShutdown
     End Sub
     ...

Listing 2: Code des COM-Add-Ins, Teil 1

Ribbon erweitern

In unserem Fall wollen wir das COM-Add-In nur nutzen, um dem Ribbon von Access einige neue Elemente hinzuzufügen. Dazu implementieren wir die Funktion GetCustomUI der Schnittstelle IRibbonUI. Diese wird von Access aus aufgerufen und wir bekommen so die Gelegenheit, Access eine Anpassung des Ribbons zu übergeben. Wir tun das wie in Listing 3, wo wir die passende Ribbon-Definition in der Variablen strXML zusammenstellen. Wer genau hinschaut, sieht, dass wir hier nicht strXML = strXML & … verwenden, sondern dass wir hier einen anderen Operator verwenden, um die Zeilen zusammenzufügen, nämlich &=.

     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"" " _
             & "loadImage=""LoadImage"" onLoad=""onLoad"">" & vbCrLf
         strXML &= "  <ribbon startFromScratch=""false"">" & vbCrLf
         strXML &= "    <tabs>" & vbCrLf
         strXML &= "      <tab idMso=""TabHomeAccess"">" & vbCrLf
         strXML &= "        <group idMso=""GroupFindAccess"" visible=""false"" />" & vbCrLf
         strXML &= "        <group id=""grpSuchenUndErsetzen"" insertAfterMso=""GroupFindAccess"" " _
             & "label=""amvSuchenUndErsetzen"">" & vbCrLf
         strXML &= "          <button id=""btnSuchen"" imageMso=""FindDialog"" size=""large"" " _
             & "onAction=""onAction"" label=""Suchen""/>" & vbCrLf
         strXML &= "          <button id=""btnErsetzen"" imageMso=""ReplaceDialog"" size=""normal"" " _
             & "onAction=""onAction"" label=""Ersetzen""/>" & vbCrLf
         strXML &= "          <control idMso=""GoToMenuAccess"" size=""normal"" />" & vbCrLf
         strXML &= "          <control idMso=""SelectMenuAccess"" size=""normal"" />" & vbCrLf
         strXML &= "        </group>" & vbCrLf
         strXML &= "      </tab>" & vbCrLf
         strXML &= "    </tabs>" & vbCrLf
         strXML &= "  </ribbon>" & vbCrLf
         strXML &= "</customUI>" & vbCrLf
         Return strXML
     End Function
     Function OnLoad(ribbon As IRibbonUI)
         Set objRibbon = ribbon
     End Function
     Public Sub OnAction(control As IRibbonControl)
         Select Case control.Id
             Case "btnSuchen"
                 objAccess.Run Environ("AppData") & "\Microsoft\AddIns\amvSuchenUndErsetzen.amvSuchen"
             Case "btnErsetzen"
                 objAccess.Run Environ("AppData") & "\Microsoft\AddIns\amvSuchenUndErsetzen.amvErsetzen"
                 Case Else
                 MsgBox "OnAction nicht behandelt: " & control.Id
         End Select
     End Sub
End Class

Listing 3: Code des COM-Add-Ins, Teil 2

Was genau erledigen wir mit dieser Ribbondefinition? Wir definieren das customUI-Element, das ribbon-Element und das tabs-Element wie in jeder herkömmlichen Ribbonerweiterung.

Danach fügen wir ein tab-Element mit dem Wert TabHomeAccess für das Attribut idMso hinzu. Damit referenzieren wir das Ribbontab mit der Beschriftung Start.

Darunter referenzieren wir ein group-Element, das den Wert GroupFindAccess für das Attribut idMso enthält. Das entspricht dem eingebauten Bereich mit den Funktionen Suchen und Ersetzen (siehe Bild 7). Dieses wollen wir ausblenden und durch unsere eigene Version ersetzen, also stellen wir das Attribut visible auf false ein.

Das zu ersetzende Ribbon-Element

Bild 7: Das zu ersetzende Ribbon-Element

Danach legen wir unsere eigene Struktur für diesen Zweck an. Das erledigen wir mit einem benutzerdefinierten group-Element mit der ID grpSuchenUndErsetzen und der Beschriftung amvSuchenUndErsetzen.

Damit diese Gruppe an der gleichen Stelle angezeigt wird wie die zuvor ausgeblendete Gruppe, fügen wir diese über das Attribut insertAfterMso genau dort ein – und zwar mit dem Wert GroupFindAccess.

Schließlich fügen wir zunächst zwei rein benutzerdefinierte button-Elemente namens btnSuchen und btnErsetzen ein – samt entsprechenden Beschriftungen.

Wie im Original soll die Suchen-Schaltfläche groß dargestellt werden und die Ersetzen-Schaltfläche klein. Damit die gleichen Icons wie beim Original erscheinen, stellen wir die Eigenschaft imageMso jeweils auf die Namen dieser Elemente ein, nämlich FindDialog und ReplaceDialog.

Danach replizieren wir noch die beiden übrigen Steuerelemente aus der Originalgruppe, indem wir button-Elemente anlegen, deren Attribut idMso wir mit den Werten GoToMenuAccess und SelectMenuAccess füllen.

Danach fügen wir die fehlenden schließenden Elemente hinzu wie zum Beispiel .

Die Anweisung Return strXML gibt die Zeichenkette aus strXML als Funktionswert an Access zurück.

Schließlich fehlen noch die Aktionen, die beim Anklicken der beiden benutzerdefinierten button-Elemente ausgelöst werden sollen.

Dazu nutzen wir die Prozedur OnAction, die wir bereits für das gleichnamige Attribut der beiden button-Elemente hinterlegt haben (onAction=“onAction“).

Diese erhält nach dem Auslösen durch Betätigen einer der Schaltflächen mit dem Parameter control einen Verweis auf das auslösende Element.

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

Schreibe einen Kommentar