twinBASIC: COM-Add-In für den VBA-Editor

Lies in den Artikel rein und unten bekommst Du ein unschlagbares Angebot!

Wer den VBA-Editor mit eigenen Tools erweitern möchte, kommt um die Programmierung von COM-Add-Ins kaum herum – zumindest nicht, wenn er eine schicke Benutzeroberfläche und die Integration in Menüs, Symbolleisten und Kontextmenüs wünscht. In diesem Artikel zeigen wir daher, wie wir die Basis für ein solches COM-Add-In mit twinBASIC programmieren. Ausgehend davon kannst Du direkt loslegen und Deine gewünschten Funktionen einbauen – es sind nur jeweils wenige Anpassung notwendig. Wir erklären Schritt für Schritt, wie die Basis des COM-Add-Ins aufgebaut ist und welche Anpassungen Du vornehmen musst, um ein COM-Add-In für Deine eigenen Anwendungen zu bauen.

Start mit Vorlage

Für den Start kannst Du das Beispielprojekt VBECOMAddInBasis.twinproj aus dem Download zu diesem Artikel verwenden. Diese Datei öffnest Du in der Entwicklungsumgebung twinBASIC, deren jeweils aktuelle Fassung Du unter dem folgenden Link findest:

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

Dort klickst Du auf Assets und kannst dann wie in Bild 1 die .zip-Datei mit der Entwicklungsumgebung herunterladen. Eine Installation ist nicht notwendig, es reicht das Entpacken im gewünschten Verzeichnis.

Download der neuesten twinBASIC-Version

Bild 1: Download der neuesten twinBASIC-Version

Damit kannst Du direkt die Datei VBECOMAddInBasis.twinproj öffnen. Diese besteht aus verschiedenen Komponenten, die wir für eine eigene Lösung ändern müssen:

  • Code des COM-Add-Ins im Modul MeinComAddIn.twin, das die eigentliche Funktionalität enthält
  • Code des Moduls dllREgistration.twin, das den Code für die Registrierung des Tools in der Registry enthält
  • Eigenschaften im Bereich Settings, zum Beispiel um den Projektnamen und Verweise anzupassen

Code der Hauptklasse

Die Hauptklasse enthält bereits alles, was zum Erstellen eines funktionsfähigen COM-Add-Ins für den VBA-Editor enthält.

Im Kopf sehen wir eine ClassId, die wir für die Registrierung benötigen. Diese müssen wir jeweils auf eine neue, auf dem jeweiligen System eindeutige GUID anpassen:

[ ClassId ("95FA1EA0-DF9F-4505-A22E-E7CFDCD189") ]

Darunter beginnt gleich die Klassendefinition. Den Namen der Klasse passen wir, genau wie den Namen des Moduls, auf den gleichen Wert an. Aktuell heißen die Klasse und die Moduldatei MeinComAddIn.

Die Klasse implementiert die Schnittstelle IDTExtensibility2:

Class MeinComAddIn ''auf eigenes Add-In anpassen
     Implements IDTExtensibility2

Konstanten für das COM-Add-In

Bevor wir diese erläutern, werfen wir einen Blick auf die benötigten Konstanten und Variablen. Hier definieren wir zunächst zwei Konstanten. Die erste enthält den Namen des Hauptmenüpunktes für Dein COM-Add-In, der zweite den Namen des Befehls, der sich in diesem Menü befindet:

Private Const cStrMenu As String = "Mein Menü"
Private Const cStrCommandButton As String = "Mein Befehl"

Der folgende Code wird dafür sorgen, dass diese beiden Konstanten beim Anlegen der Menübefehls und des Kontextmenü-Eintrags berücksichtigt werden. Der Menübefehl wird dann wie in Bild 2 auftauchen.

Hier soll der Menübefehl erscheinen.

Bild 2: Hier soll der Menübefehl erscheinen.

Den Kontextmenü-Eintrag finden wir, wenn wir mit der rechten Maustaste in das Code-Fenster klicken (siehe Bild 3).

Der Kontextmenübefehl wird hier eingeblendet.

Bild 3: Der Kontextmenübefehl wird hier eingeblendet.

Variablen für das COM-Add-In

Für die Basisversion des COM-Add-Ins verwenden wir die folgenden Variablen:

  • objVBE: Nimmt einen Verweis auf den Visual Basic Editor auf.
  • objAddIn: Referenziert das Add-In.
  • cbc: Referenziert das CommandBarButton-Objekt für den Button in der Menüleiste.
  • cbcContext: Referenziert das CommandBarButton-Objekt für den Button in der Kontextmenü-Leiste.
  • cbcEvents: Nimmt die Events für den Button in der Menüleiste auf.
  • cbcEventsContext: Nimmt die Events für den Button in der Kontextmenü-Leiste auf.

Die Deklaration sieht wie folgt aus:

Private objVBE As VBIDE.VBE
Private objAddIn As VBIDE.AddIn
Private cbc As CommandBarButton
Private cbcContext As CommandBarButton
Private WithEvents cbcEvents As VBIDE.CommandBarEvents
Private WithEvents cbcEventsContext As VBIDE.CommandBarEvents

Schließlich benötigen wir noch eine Variable, mit der wir die Information speichern, ob das Add-In geladen ist:

Private isConnected As Boolean   

Beim Laden/Verbinden des COM-Add-Ins

Weiter oben haben wir geschrieben, dass die Klasse MeinComAddIn die Schnittstelle IDTExtensibility2 implementiert.

Das bedeutet, dass wir einerseits bestimmte Ereignisprozeduren implementieren müssen. Andererseits können wir aber auch sichergehen, dass diese Ereignisprozeduren ausgelöst werden, wenn der VBA-Editor für eine der Office-Anwendungen gestartet wird und unser COM-Add-In in der Registry eingetragen ist.

Die erste und wichtigste Prozedur, die dadurch aufgerufen wird, heißt OnConnection. Sie sieht wie folgt aus:

Sub OnConnection(ByVal Application As Object, _
         ByVal ConnectMode As ext_ConnectMode, _
         ByVal AddInInst As Object, _
         ByRef custom As Variant()) _
         Implements IDTExtensibility2.OnConnection
     Set objVBE = Application
     Set objAddIn = AddInInst
     isConnected = True
     CreateToolBar()
End Sub

Die Prozedur hat vor allem eine Aufgabe: Einen Verweis auf die Klasse des VBA-Editors entgegenzunehmen, der mit dem Parameter Application geliefert wird, und diesen mit der Variablen objVBE zu referenzieren.

Diese Variable ist der Ausgangspunkt für jegliche Aktionen, die wir im VBA-Editor codegesteuert durch unser Add-In durchführen wollen.

Außerdem referenzieren wir noch die Add-In-Instanz selbst, stellen isConnected auf True ein und rufen die Prozedur CreateToolBar auf, die wiederum die Menü- und Kontextmenü-Leisten erstellt.

Schaltflächen zu Menü und Kontextmenü hinzufügen

In der Regel wollen wir durch COM-Add-Ins im VBA-Editor Operationen am Code durchführen. Deshalb gehen wir in dieser Basisversion davon aus, dass wir den gleichen Befehl einmal in der Menüleiste und einmal im Kontextmenü hinzufügen wollen. Das erledigen wir mit der Prozedur CreateToolBar aus Listing 1.

Private Sub CreateToolBar()
     Dim cbr As CommandBar
     Dim cbp As CommandBarPopup
     Dim cbpContext As CommandBarPopup
     ''Eintrag in Menüleiste anlegen
     Set cbr = objVBE.CommandBars("Menüleiste")
     On Error Resume Next
     Set cbp = cbr.Controls(cStrMenu)
     On Error GoTo 0
     If cbp Is Nothing Then
         Set cbp = cbr.Controls.Add(msoControlPopup, Temporary:=True)
         With cbp
             .Move Before:=8
             .Caption = cStrMenu
             Set cbb =.Controls.Add(msoControlButton, Temporary:=True)
         End With
     Else
         Set cbb = cbp.Controls.Add(msoControlButton, temporary:=True)
     End If
     With cbb
         .Caption = cStrCommandButton
         .BeginGroup = True
         Set cbbEvents = objVBE.Events.CommandBarEvents(cbb)
     End With
     ''Eintrag in Kontextmenü anlegen
     On Error Resume Next
     Set cbpContext = objVBE.CommandBars("Code Window").Controls(cStrMenu)
     On Error GoTo 0
     If cbpContext Is Nothing Then
         Set cbpContext = objVBE.CommandBars("Code Window").Controls.Add(msoControlPopup, Temporary:=True)
         With cbpContext
             .Caption = cStrMenu
         End With
     End If
     Set cbbContext = cbpContext.Controls.Add(Temporary:=True)
     With cbbContext
         .Caption = cStrCommandButton
         .BeginGroup = True
         Set cbbEventsContext = objVBE.Events.CommandBarEvents(cbbContext)
     End With
End Sub

Listing 1: Hinzufügen von Schaltflächen zu Menüleiste und Kontextmenü

Die Prozedur deklariert zunächst zwei Untermenü-Elemente namens cbp (für das Hauptmenü) und cbpContext (für das Kontextmenü) mit dem Typ CommandBarPopup.

Untermenü referenzieren oder zur Menüleiste hinzufügen

Dann versucht sie, ein eventuell bereits vorhandenes Untermenü mit dem Namen aus cStrMenu, in dieser Basisversion also Mein Menü, zu referenzieren. Warum das? Weil es sein kann, dass wir bereits mehrere andere COM-Add-Ins in einem Untermenü untergebracht haben, in dem wir die Funktionen all unserer COM-Add-Ins aufrufen wollen. Also versuchen wir bei deaktivierter Fehlerbehandlung, ein solches Untermenü zu referenzieren.

Direkt danach prüfen wir, ob dies erfolgreich war – dann wäre cbp gefüllt. Falls nicht, erstellen wir das Element mit der Add-Methode der Controls-Auflistung der Menüleiste neu und referenzieren es mit der Variablen cbp. Wir bewegen es mit der Move-Methode an die achte Stelle im Menü, also relativ weit nach rechts. Dies kannst Du individuell anpassen.

Außerdem stellen wir die Beschriftung über die Caption-Eigenschaft auf den Wert aus cStrMenu ein, in diesem Fall also Mein Menü.

Schaltfläche zum Untermenü der Menüleiste hinzufügen

Diesem fügen wir mit der Add-Methode der Controls-Auflistung eine neue Schaltfläche hinzu und legen für diese die Beschriftung aus der Konstanten cStrCommandButton fest, in diesem Fall Mein Befehl. Außerdem beginnen wir hier eine neue Gruppe und stellen die Variable cbcEvents auf die Events-Auflistung für die Schaltfläche ein. Dadurch können wir später für dieses Objekt die Ereignisse der Schaltflächen implementieren.

Untermenü und Schaltfläche zum Kontextmenü hinzufügen

Auf ähnliche Weise fügen wir das Untermenü und die Schaltfläche ein. Damit erhalten wir ebenfalls eine Objektvariable namens cbcEventsContext, mit der wir das Ereignis beim Anklicken der Schaltfläche implementieren können.

Weitere Methoden der IDTExtensibility2-Schnittstelle

Wir programmieren noch weitere Methoden der oben vorgestellten Schnittstelle. Die erste namens OnDisconnection ruft die Prozedur ShutdownAddin auf:

Sub OnDisconnection( _
         ByVal RemoveMode As ext_DisconnectMode, _
         ByRef custom As Variant()) _
         Implements IDTExtensibility2.OnDisconnection
     ShutdownAddin()
End Sub

Die zweite rufe diese ebenfalls auf:

Sub OnBeginShutdown(ByRef custom As Variant()) _
         Implements IDTExtensibility2.OnBeginShutdown
     ShutdownAddin()     
End Sub

Die beiden übrigen Methoden haben keine Funktion, aber wir müssen immer alle Methoden einer Schnittstelle implementieren:

Sub OnAddInsUpdate(ByRef custom As Variant()) _
     Implements IDTExtensibility2.OnAddInsUpdate
                         
End Sub
                
Sub OnStartupComplete(ByRef custom As Variant()) _
     Implements IDTExtensibility2.OnStartupComplete
End Sub

Beim Herunterfahren des COM-Add-Ins

Gleich zwei der Prozeduren der Schnittstelle IDTExtensibility rufen die Prozedur ShutdownAddin auf. Diese Prozedur leert die Objektvariablen, damit diese nicht im Speicher verbleiben und auf irgendeine Weise das Entladen des COM-Add-Ins behindern können. Da wir diese Prozedur zur Sicherheit gleich von zwei Prozeduren der Schnittstelle aus aufrufen, setzen wir die Variable isConnected nach dem ersten Aufruf auf False, damit die Prozedur beim zweiten Aufruf direkt verlassen werden kann:

 

Schreibe einen Kommentar