{"id":55000421,"date":"2024-04-01T00:00:00","date_gmt":"2024-04-23T12:48:02","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=421"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"twinBASIC_COMAddIn_fuer_den_VBAEditor","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/twinBASIC_COMAddIn_fuer_den_VBAEditor\/","title":{"rendered":"twinBASIC: COM-Add-In f&uuml;r den VBA-Editor"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg08.met.vgwort.de\/na\/3a04e8b641c945dc84440e14d105707b\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Wer den VBA-Editor mit eigenen Tools erweitern m&ouml;chte, kommt um die Programmierung von COM-Add-Ins kaum herum &#8211; zumindest nicht, wenn er eine schicke Benutzeroberfl&auml;che und die Integration in Men&uuml;s, Symbolleisten und Kontextmen&uuml;s w&uuml;nscht. In diesem Artikel zeigen wir daher, wie wir die Basis f&uuml;r ein solches COM-Add-In mit twinBASIC programmieren. Ausgehend davon kannst Du direkt loslegen und Deine gew&uuml;nschten Funktionen einbauen &#8211; es sind nur jeweils wenige Anpassung notwendig. Wir erkl&auml;ren Schritt f&uuml;r Schritt, wie die Basis des COM-Add-Ins aufgebaut ist und welche Anpassungen Du vornehmen musst, um ein COM-Add-In f&uuml;r Deine eigenen Anwendungen zu bauen.<\/b><\/p>\n<h2>Start mit Vorlage<\/h2>\n<p>F&uuml;r den Start kannst Du das Beispielprojekt <b>VBECOMAddInBasis.twinproj <\/b>aus dem Download zu diesem Artikel verwenden. Diese Datei &ouml;ffnest Du in der Entwicklungsumgebung twinBASIC, deren jeweils aktuelle Fassung Du unter dem folgenden Link findest:<\/p>\n<pre>https:\/\/github.com\/twinbasic\/twinbasic\/releases<\/pre>\n<p>Dort klickst Du auf Assets und kannst dann wie in Bild 1 die <b>.zip<\/b>-Datei mit der Entwicklungsumgebung herunterladen. Eine Installation ist nicht notwendig, es reicht das Entpacken im gew&uuml;nschten Verzeichnis.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_421_001.png\" alt=\"Download der neuesten twinBASIC-Version\" width=\"700\" height=\"429.6263\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Download der neuesten twinBASIC-Version<\/span><\/b><\/p>\n<p>Damit kannst Du direkt die Datei <b>VBECOMAddInBasis.twinproj <\/b>&ouml;ffnen. Diese besteht aus verschiedenen Komponenten, die wir f&uuml;r eine eigene L&ouml;sung &auml;ndern m&uuml;ssen:<\/p>\n<ul>\n<li>Code des COM-Add-Ins im Modul <b>MeinComAddIn.twin<\/b>, das die eigentliche Funktionalit&auml;t enth&auml;lt<\/li>\n<li>Code des Moduls <b>dllREgistration.twin<\/b>, das den Code f&uuml;r die Registrierung des Tools in der Registry enth&auml;lt<\/li>\n<li>Eigenschaften im Bereich <b>Settings<\/b>, zum Beispiel um den Projektnamen und Verweise anzupassen<\/li>\n<\/ul>\n<h2>Code der Hauptklasse<\/h2>\n<p>Die Hauptklasse enth&auml;lt bereits alles, was zum Erstellen eines funktionsf&auml;higen COM-Add-Ins f&uuml;r den VBA-Editor enth&auml;lt.<\/p>\n<p>Im Kopf sehen wir eine <b>ClassId<\/b>, die wir f&uuml;r die Registrierung ben&ouml;tigen. Diese m&uuml;ssen wir jeweils auf eine neue, auf dem jeweiligen System eindeutige GUID anpassen:<\/p>\n<pre>[ ClassId (\"95FA1EA0-DF9F-4505-A22E-E7CFDCD189\") ]<\/pre>\n<p>Darunter beginnt gleich die Klassendefinition. Den Namen der Klasse passen wir, genau wie den Namen des Moduls, auf den gleichen Wert an. Aktuell hei&szlig;en die Klasse und die Moduldatei <b>MeinComAddIn<\/b>.<\/p>\n<p>Die Klasse implementiert die Schnittstelle <b>IDTExtensibility2<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Class<\/span> MeinComAddIn ''auf eigenes Add-In anpassen\r\n     Implements IDTExtensibility2<\/pre>\n<h2>Konstanten f&uuml;r das COM-Add-In<\/h2>\n<p>Bevor wir diese erl&auml;utern, werfen wir einen Blick auf die ben&ouml;tigten Konstanten und Variablen. Hier definieren wir zun&auml;chst zwei Konstanten. Die erste enth&auml;lt den Namen des Hauptmen&uuml;punktes f&uuml;r Dein COM-Add-In, der zweite den Namen des Befehls, der sich in diesem Men&uuml; befindet:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>Const cStrMenu<span style=\"color:blue;\"> As String<\/span> = \"Mein Men&uuml;\"\r\n<span style=\"color:blue;\">Private <\/span>Const cStrCommandButton<span style=\"color:blue;\"> As String<\/span> = \"Mein Befehl\"<\/pre>\n<p>Der folgende Code wird daf&uuml;r sorgen, dass diese beiden Konstanten beim Anlegen der Men&uuml;befehls und des Kontextmen&uuml;-Eintrags ber&uuml;cksichtigt werden. Der Men&uuml;befehl wird dann wie in Bild 2 auftauchen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_421_002.png\" alt=\"Hier soll der Men&uuml;befehl erscheinen.\" width=\"649.627\" height=\"137.403\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Hier soll der Men&uuml;befehl erscheinen.<\/span><\/b><\/p>\n<p>Den Kontextmen&uuml;-Eintrag finden wir, wenn wir mit der rechten Maustaste in das Code-Fenster klicken (siehe Bild 3).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_421_003.png\" alt=\"Der Kontextmen&uuml;befehl wird hier eingeblendet.\" width=\"424.6267\" height=\"428.9508\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Der Kontextmen&uuml;befehl wird hier eingeblendet.<\/span><\/b><\/p>\n<h2>Variablen f&uuml;r das COM-Add-In<\/h2>\n<p>F&uuml;r die Basisversion des COM-Add-Ins verwenden wir die folgenden Variablen:<\/p>\n<ul>\n<li><b>objVBE<\/b>: Nimmt einen Verweis auf den Visual Basic Editor auf.<\/li>\n<li><b>objAddIn<\/b>: Referenziert das Add-In.<\/li>\n<li><b>cbc<\/b>: Referenziert das <b>CommandBarButton<\/b>-Objekt f&uuml;r den Button in der Men&uuml;leiste.<\/li>\n<li><b>cbcContext<\/b>: Referenziert das <b>CommandBarButton<\/b>-Objekt f&uuml;r den Button in der Kontextmen&uuml;-Leiste.<\/li>\n<li><b>cbcEvents<\/b>: Nimmt die Events f&uuml;r den Button in der Men&uuml;leiste auf.<\/li>\n<li><b>cbcEventsContext<\/b>: Nimmt die Events f&uuml;r den Button in der Kontextmen&uuml;-Leiste auf.<\/li>\n<\/ul>\n<p>Die Deklaration sieht wie folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>objVBE<span style=\"color:blue;\"> As <\/span>VBIDE.VBE\r\n<span style=\"color:blue;\">Private <\/span>objAddIn<span style=\"color:blue;\"> As <\/span>VBIDE.AddIn\r\n<span style=\"color:blue;\">Private <\/span>cbc<span style=\"color:blue;\"> As <\/span>CommandBarButton\r\n<span style=\"color:blue;\">Private <\/span>cbcContext<span style=\"color:blue;\"> As <\/span>CommandBarButton\r\n<span style=\"color:blue;\">Private <\/span>WithEvents cbcEvents<span style=\"color:blue;\"> As <\/span>VBIDE.CommandBarEvents\r\n<span style=\"color:blue;\">Private <\/span>WithEvents cbcEventsContext<span style=\"color:blue;\"> As <\/span>VBIDE.CommandBarEvents<\/pre>\n<p>Schlie&szlig;lich ben&ouml;tigen wir noch eine Variable, mit der wir die Information speichern, ob das Add-In geladen ist:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>isConnected<span style=\"color:blue;\"> As Boolean<\/span>   <\/pre>\n<h2>Beim Laden\/Verbinden des COM-Add-Ins<\/h2>\n<p>Weiter oben haben wir geschrieben, dass die Klasse <b>MeinComAddIn <\/b>die Schnittstelle <b>IDTExtensibility2 <\/b>implementiert.<\/p>\n<p>Das bedeutet, dass wir einerseits bestimmte Ereignisprozeduren implementieren m&uuml;ssen. Andererseits k&ouml;nnen wir aber auch sichergehen, dass diese Ereignisprozeduren ausgel&ouml;st werden, wenn der VBA-Editor f&uuml;r eine der Office-Anwendungen gestartet wird und unser COM-Add-In in der Registry eingetragen ist.<\/p>\n<p>Die erste und wichtigste Prozedur, die dadurch aufgerufen wird, hei&szlig;t <b>OnConnection<\/b>. Sie sieht wie folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Sub <\/span>OnConnection(ByVal Application<span style=\"color:blue;\"> As Object<\/span>, _\r\n         ByVal ConnectMode<span style=\"color:blue;\"> As <\/span>ext_ConnectMode, _\r\n         ByVal AddInInst<span style=\"color:blue;\"> As Object<\/span>, _\r\n         ByRef custom<span style=\"color:blue;\"> As Variant<\/span>()) _\r\n         Implements IDTExtensibility2.OnConnection\r\n     <span style=\"color:blue;\">Set<\/span> objVBE = Application\r\n     <span style=\"color:blue;\">Set<\/span> objAddIn = AddInInst\r\n     isConnected = <span style=\"color:blue;\">True<\/span>\r\n     CreateToolBar()\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die Prozedur hat vor allem eine Aufgabe: Einen Verweis auf die Klasse des VBA-Editors entgegenzunehmen, der mit dem Parameter <b>Application<\/b> geliefert wird, und diesen mit der Variablen <b>objVBE <\/b>zu referenzieren.<\/p>\n<p>Diese Variable ist der Ausgangspunkt f&uuml;r jegliche Aktionen, die wir im VBA-Editor codegesteuert durch unser Add-In durchf&uuml;hren wollen.<\/p>\n<p>Au&szlig;erdem referenzieren wir noch die Add-In-Instanz selbst, stellen <b>isConnected <\/b>auf <b>True <\/b>ein und rufen die Prozedur <b>CreateToolBar <\/b>auf, die wiederum die Men&uuml;- und Kontextmen&uuml;-Leisten erstellt.<\/p>\n<h2>Schaltfl&auml;chen zu Men&uuml; und Kontextmen&uuml; hinzuf&uuml;gen<\/h2>\n<p>In der Regel wollen wir durch COM-Add-Ins im VBA-Editor Operationen am Code durchf&uuml;hren. Deshalb gehen wir in dieser Basisversion davon aus, dass wir den gleichen Befehl einmal in der Men&uuml;leiste und einmal im Kontextmen&uuml; hinzuf&uuml;gen wollen. Das erledigen wir mit der Prozedur <b>CreateToolBar<\/b> aus Listing 1.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>CreateToolBar()\r\n     <span style=\"color:blue;\">Dim <\/span>cbr<span style=\"color:blue;\"> As <\/span>CommandBar\r\n     <span style=\"color:blue;\">Dim <\/span>cbp<span style=\"color:blue;\"> As <\/span>CommandBarPopup\r\n     <span style=\"color:blue;\">Dim <\/span>cbpContext<span style=\"color:blue;\"> As <\/span>CommandBarPopup\r\n     ''Eintrag in Men&uuml;leiste anlegen\r\n     <span style=\"color:blue;\">Set<\/span> cbr = objVBE.CommandBars(\"Men&uuml;leiste\")\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> cbp = cbr.Controls(cStrMenu)\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n     <span style=\"color:blue;\">If <\/span>cbp Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> cbp = cbr.Controls.Add(msoControlPopup, Temporary:=<span style=\"color:blue;\">True<\/span>)\r\n         <span style=\"color:blue;\">With<\/span> cbp\r\n             .Move Before:=8\r\n             .Caption = cStrMenu\r\n             <span style=\"color:blue;\">Set<\/span> cbb =.Controls.Add(msoControlButton, Temporary:=<span style=\"color:blue;\">True<\/span>)\r\n         End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> cbb = cbp.Controls.Add(msoControlButton, temporary:=<span style=\"color:blue;\">True<\/span>)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">With<\/span> cbb\r\n         .Caption = cStrCommandButton\r\n         .BeginGroup = <span style=\"color:blue;\">True<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> cbbEvents = objVBE.Events.CommandBarEvents(cbb)\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     ''Eintrag in Kontextmen&uuml; anlegen\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> cbpContext = objVBE.CommandBars(\"Code Window\").Controls(cStrMenu)\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n     <span style=\"color:blue;\">If <\/span>cbpContext Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> cbpContext = objVBE.CommandBars(\"Code Window\").Controls.Add(msoControlPopup, Temporary:=<span style=\"color:blue;\">True<\/span>)\r\n         <span style=\"color:blue;\">With<\/span> cbpContext\r\n             .Caption = cStrMenu\r\n         End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> cbbContext = cbpContext.Controls.Add(Temporary:=<span style=\"color:blue;\">True<\/span>)\r\n     <span style=\"color:blue;\">With<\/span> cbbContext\r\n         .Caption = cStrCommandButton\r\n         .BeginGroup = <span style=\"color:blue;\">True<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> cbbEventsContext = objVBE.Events.CommandBarEvents(cbbContext)\r\n     End <span style=\"color:blue;\">With<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Hinzuf&uuml;gen von Schaltfl&auml;chen zu Men&uuml;leiste und Kontextmen&uuml;<\/span><\/b><\/p>\n<p>Die Prozedur deklariert zun&auml;chst zwei Untermen&uuml;-Elemente namens <b>cbp <\/b>(f&uuml;r das Hauptmen&uuml;) und <b>cbpContext <\/b>(f&uuml;r das Kontextmen&uuml;) mit dem Typ <b>CommandBarPopup<\/b>.<\/p>\n<h2>Untermen&uuml; referenzieren oder zur Men&uuml;leiste hinzuf&uuml;gen<\/h2>\n<p>Dann versucht sie, ein eventuell bereits vorhandenes Untermen&uuml; mit dem Namen aus <b>cStrMenu<\/b>, in dieser Basisversion also <b>Mein Men&uuml;<\/b>, zu referenzieren. Warum das? Weil es sein kann, dass wir bereits mehrere andere COM-Add-Ins in einem Untermen&uuml; untergebracht haben, in dem wir die Funktionen all unserer COM-Add-Ins aufrufen wollen. Also versuchen wir bei deaktivierter Fehlerbehandlung, ein solches Untermen&uuml; zu referenzieren.<\/p>\n<p>Direkt danach pr&uuml;fen wir, ob dies erfolgreich war &#8211; dann w&auml;re <b>cbp <\/b>gef&uuml;llt. Falls nicht, erstellen wir das Element mit der <b>Add<\/b>-Methode der <b>Controls<\/b>-Auflistung der Men&uuml;leiste neu und referenzieren es mit der Variablen <b>cbp<\/b>. Wir bewegen es mit der <b>Move<\/b>-Methode an die achte Stelle im Men&uuml;, also relativ weit nach rechts. Dies kannst Du individuell anpassen.<\/p>\n<p>Au&szlig;erdem stellen wir die Beschriftung &uuml;ber die <b>Caption<\/b>-Eigenschaft auf den Wert aus <b>cStrMenu <\/b>ein, in diesem Fall also <b>Mein Men&uuml;<\/b>.<\/p>\n<h2>Schaltfl&auml;che zum Untermen&uuml; der Men&uuml;leiste hinzuf&uuml;gen<\/h2>\n<p>Diesem f&uuml;gen wir mit der <b>Add<\/b>-Methode der <b>Controls<\/b>-Auflistung eine neue Schaltfl&auml;che hinzu und legen f&uuml;r diese die Beschriftung aus der Konstanten <b>cStrCommandButton <\/b>fest, in diesem Fall <b>Mein Befehl<\/b>. Au&szlig;erdem beginnen wir hier eine neue Gruppe und stellen die Variable <b>cbcEvents <\/b>auf die <b>Events<\/b>-Auflistung f&uuml;r die Schaltfl&auml;che ein. Dadurch k&ouml;nnen wir sp&auml;ter f&uuml;r dieses Objekt die Ereignisse der Schaltfl&auml;chen implementieren.<\/p>\n<h2>Untermen&uuml; und Schaltfl&auml;che zum Kontextmen&uuml; hinzuf&uuml;gen<\/h2>\n<p>Auf &auml;hnliche Weise f&uuml;gen wir das Untermen&uuml; und die Schaltfl&auml;che ein. Damit erhalten wir ebenfalls eine Objektvariable namens <b>cbcEventsContext<\/b>, mit der wir das Ereignis beim Anklicken der Schaltfl&auml;che implementieren k&ouml;nnen.<\/p>\n<h2>Weitere Methoden der IDTExtensibility2-Schnittstelle<\/h2>\n<p>Wir programmieren noch weitere Methoden der oben vorgestellten Schnittstelle. Die erste namens <b>OnDisconnection <\/b>ruft die Prozedur <b>ShutdownAddin <\/b>auf:<\/p>\n<pre><span style=\"color:blue;\">Sub <\/span>OnDisconnection( _\r\n         ByVal RemoveMode<span style=\"color:blue;\"> As <\/span>ext_DisconnectMode, _\r\n         ByRef custom<span style=\"color:blue;\"> As Variant<\/span>()) _\r\n         Implements IDTExtensibility2.OnDisconnection\r\n     ShutdownAddin()\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die zweite rufe diese ebenfalls auf:<\/p>\n<pre><span style=\"color:blue;\">Sub <\/span>OnBeginShutdown(ByRef custom<span style=\"color:blue;\"> As Variant<\/span>()) _\r\n         Implements IDTExtensibility2.OnBeginShutdown\r\n     ShutdownAddin()     \r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die beiden &uuml;brigen Methoden haben keine Funktion, aber wir m&uuml;ssen immer alle Methoden einer Schnittstelle implementieren:<\/p>\n<pre><span style=\"color:blue;\">Sub <\/span>OnAddInsUpdate(ByRef custom<span style=\"color:blue;\"> As Variant<\/span>()) _\r\n     Implements IDTExtensibility2.OnAddInsUpdate\r\n                         \r\n<span style=\"color:blue;\">End Sub<\/span>\r\n                \r\n<span style=\"color:blue;\">Sub <\/span>OnStartupComplete(ByRef custom<span style=\"color:blue;\"> As Variant<\/span>()) _\r\n     Implements IDTExtensibility2.OnStartupComplete\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Beim Herunterfahren des COM-Add-Ins<\/h2>\n<p>Gleich zwei der Prozeduren der Schnittstelle <b>IDTExtensibility <\/b>rufen die Prozedur <b>ShutdownAddin <\/b>auf. Diese Prozedur leert die Objektvariablen, damit diese nicht im Speicher verbleiben und auf irgendeine Weise das Entladen des COM-Add-Ins behindern k&ouml;nnen. Da wir diese Prozedur zur Sicherheit gleich von zwei Prozeduren der Schnittstelle aus aufrufen, setzen wir die Variable <b>isConnected <\/b>nach dem ersten Aufruf auf <b>False<\/b>, damit die Prozedur beim zweiten Aufruf direkt verlassen werden kann:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>ShutdownAddin()\r\n     <span style=\"color:blue;\">If <\/span>isConnected = <span style=\"color:blue;\">False<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Exit Sub<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> cbbEvents = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> cbb = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> cbbEventsContext = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> cbbContext = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> objAddIn = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> objVBE = Nothing\r\n     isConnected = <span style=\"color:blue;\">False<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Aufruf der eigentlichen Funktion des COM-Add-Ins<\/h2>\n<p>Die Funktion, die wir eigentlich aufrufen wollen, sieht wie folgt aus &#8211; und sie soll lediglich zu Testzwecken ein Meldungsfenster anzeigen:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>LaunchFunction()\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Mein COM-Add-In\"\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Aber von wo aus wird diese aufgerufen? F&uuml;r diesen Zweck implementieren wir die Prozeduren, die durch das <b>Click<\/b>-Ereignis der Objekte aus <b>cbcEvents <\/b>und <b>cbcEventsContext <\/b>aus aufgerufen werden. Diese Prozeduren erhalten vor allem den Parameter <b>CommandBarControl<\/b>. Dieser liefert einen Verweis auf die angeklickte Schaltfl&auml;che. Damit k&ouml;nnen wir beispielsweise den Zustand des aufrufenden Steuerelements &auml;ndern. Der zweite Parameter <b>Handled <\/b>kann genutzt werden, wenn mehrere COM-Add-Ins ein Ereignis f&uuml;r ein eingebautes Steuerelement oder auch ein benutzerdefinierte Steuerelement implementieren. Durch das Setzen von <b>Handled <\/b>auf <b>True <\/b>signalisieren wir weiteren Aufrufen, dass das Ereignis bereits behandelt wurde. Der dritte Parameter <b>CancelDefault <\/b>ist f&uuml;r den Fall gedacht, dass wir ein Ereignis f&uuml;r ein eingebautes Steuerelement setzen. Stellen wir diesen Parameter auf <b>True <\/b>ein, wird die eingebaute Funktion durch unsere Funktion ersetzt.<\/p>\n<p>Wir wollen in unseren Implementierungen f&uuml;r die Schaltfl&auml;che in der Men&uuml;leiste und im Kontextmen&uuml; vorrangig unsere Prozedur <b>LaunchFunction <\/b>aufrufen:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cbcEvents_Click( _\r\n         ByVal CommandBarControl<span style=\"color:blue;\"> As Object<\/span>, _\r\n         ByRef Handled<span style=\"color:blue;\"> As Boolean<\/span>, _\r\n        ByRef CancelDefault<span style=\"color:blue;\"> As Boolean<\/span>) _\r\n        Handles cbcEvents.Click\r\n     LaunchFunction\r\n     Handled = <span style=\"color:blue;\">True<\/span>\r\n     CancelDefault = <span style=\"color:blue;\">False<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>cbcEventsContext_Click( _\r\n         ByVal CommandBarControl<span style=\"color:blue;\"> As Object<\/span>, _\r\n         ByRef Handled<span style=\"color:blue;\"> As Boolean<\/span>, _\r\n         ByRef CancelDefault<span style=\"color:blue;\"> As Boolean<\/span>) _\r\n         Handles cbcEventsContext.Click\r\n     LaunchFunction\r\n     Handled = <span style=\"color:blue;\">True<\/span>\r\n     CancelDefault = <span style=\"color:blue;\">False<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Erstellen eigener COM-Add-Ins f&uuml;r den VBA-Editor auf Basis der Vorlage<\/h2>\n<p>Nun wollen wir ein eigenes COM-Add-In erstellen und schauen uns an, welche &Auml;nderungen wir daf&uuml;r im Projekt vornehmen m&uuml;ssen.<\/p>\n<p>Als Erstes legen wir dazu eine Kopie der Vorlage <b>VBECOMAddInBasis.twinproj <\/b>unter dem gew&uuml;nschten Namen an, in diesem Beispiel unter dem Namen <b>EigenesCOMAddIn.twinproj<\/b>.<\/p>\n<p>Dann &ouml;ffnen wir dieses in <b>twinBASIC<\/b> und nehmen die erforderlichen Anpassungen f&uuml;r den Betrieb als eigenes COM-Add-In f&uuml;r den VBA-Editor vor:<\/p>\n<ul>\n<li>Projekteinstellungen<\/li>\n<li>Registrierungseinstellungen<\/li>\n<li>Umbenennen der Klasse und der Klassendatei und &Auml;ndern der ClassID<\/li>\n<li>Codeanpassungen: Men&uuml;eintr&auml;ge und Funktionen<\/li>\n<li>Gegebenenfalls Benutzeroberfl&auml;che hinzuf&uuml;gen<\/li>\n<\/ul>\n<h2>Anpassen der Projekteinstellungen<\/h2>\n<p>Bei den Projekteinstellungen legen f&uuml;r f&uuml;r den Projektnamen und den Projekttitel den gew&uuml;nschten Namen fest. Au&szlig;erdem k&ouml;nnen wir die Beschreibung anpassen (siehe Bild 4).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_421_004.png\" alt=\"Einstellen der Projekteigenschaften\" width=\"700\" height=\"322.7234\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Einstellen der Projekteigenschaften<\/span><\/b><\/p>\n<h2>Anpassen der Registrierungseinstellungen<\/h2>\n<p>Die Registrierung und das Aufheben der Registrierung werden &uuml;ber die beiden Funktionen <b>DllRegisterServer <\/b>und <b>DllUnregisterServer <\/b>im Modul <b>dllRegistration.twin <\/b>erledigt. Wenn wir das Projekt &uuml;ber die Schaltfl&auml;che <b>Build <\/b>erstellen, wird nicht nur die DLL erstellt, sondern auch die <b>DllRegisterServer<\/b>-Funktion aufgerufen. Vorher wird auch noch <b>DllUnregisterServer <\/b>gestartet, damit eventuell bereits vorhandene Registrierungsinformationen entfernt werden.<\/p>\n<p>Diese beiden Funktionen werden auch aufgerufen, wenn wir die DLL auf den Zielrechner kopieren und dort &uuml;ber die Eingabeaufforderung das Programm <b>RegSvr32.exe <\/b>aufrufen. Bei normalem Aufruf wird <b>DllRegisterServer <\/b>verwendet, wenn wir den Parameter <b>\/u <\/b>zum Aufheben der Registrierung angeben, wird <b>DllUnregisterServer <\/b>genutzt.<\/p>\n<p>F&uuml;r die Registrierung werden weitgehend die Informationen aus den Projekteinstellungen genutzt. Lediglich den Namen der Add-In-Klasse m&uuml;ssen wir an den Namen anpassen, den wir gleich noch f&uuml;r die Klasse festlegen. Dies erledigen wir im Modul <b>dllRegistration <\/b>in der Zeile, welche den Wert der Konstanten <b>AddinClassName <\/b>definiert (siehe Bild 5). Den hier angegebenen Klassennamen merken wir uns f&uuml;r den n&auml;chsten Schritt.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_421_005.png\" alt=\"Einstellungen f&uuml;r die Registrierung\" width=\"700\" height=\"209.2645\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Einstellungen f&uuml;r die Registrierung<\/span><\/b><\/p>\n<h2>Umbenennen der Klasse und der Klassendatei und &Auml;ndern der ClassID<\/h2>\n<p>Hier sind drei Schritte n&ouml;tig. Als Erstes legen wir f&uuml;r die Klassendatei, die wir im Projektexplorer unter <b>Sources <\/b>finden, mit dem Kontextmen&uuml;-Befehl <b>Rename <\/b>einen neuen Namen fest. Dieser sollte auf den Namen der oben angegebenen Klasse lauten &#8211; plus die Dateiendung <b>.twin<\/b> (siehe Bild 6).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_421_006.png\" alt=\"Umbenennen des Klassennamens\" width=\"424.6267\" height=\"464.6216\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Umbenennen des Klassennamens<\/span><\/b><\/p>\n<p>Dann &ouml;ffnen wir dieses Modul und erledigen dort zwei Schritte:<\/p>\n<ul>\n<li>Einstellen einer neuen GUID f&uuml;r die <b>ClassId<\/b><\/li>\n<li>&Auml;ndern des Klassennamens hinter dem Schl&uuml;sselwort <b>Class <\/b>auf den neuen Namen (siehe Bild 7)<\/li>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_421_007.png\" alt=\"Einstellen von GUID und Klassenname\" width=\"549.6265\" height=\"149.977\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Einstellen von GUID und Klassenname<\/span><\/b><\/p>\n<\/ul>\n<h2>Codeanpassungen: Men&uuml;eintr&auml;ge und Funktionen<\/h2>\n<p>Damit kommen wir bereits zu den interessanten Aufgaben &#8211; dem Entwerfen der eigenen Funktionen. Dazu ben&ouml;tigen wir als Erstes entsprechende Eintr&auml;ge in Men&uuml;s und Untermen&uuml;s.<\/p>\n<p>Dabei m&uuml;ssen wir entscheiden, ob wir einen eigenen Men&uuml;punkt im Hauptmen&uuml; einbauen oder ob wir einen Eintrag in einem der vorhandenen Men&uuml;s integrieren wollen. Im Beispiel haben wir einfach einen neuen Hauptmen&uuml;punkt hinzugef&uuml;gt. Auch das Hinzuf&uuml;gen von Befehlen zu einem der vorhandenen Hauptmen&uuml;punkte w&auml;re einfach machbar, denn wir brauchen das jeweilige Men&uuml; einfach nur &uuml;ber die Bezeichnung zu referenzieren, die als Men&uuml;name angezeigt wird.<\/p>\n<p>Spannender wird es bei den Kontextmen&uuml;s, denn diese zeigen keine Bezeichnung oder einen Namen an. Wir k&ouml;nnen aber leicht herausfinden, wie eines der Kontextmen&uuml;s hei&szlig;t. Dazu f&uuml;gen wir einfach allen Kontextmen&uuml;s per VBA einen Befehl hinzu, der den Namen enth&auml;lt. Wir brauchen dann nur noch das gew&uuml;nschte Kontextmen&uuml; per Rechtsklick zu &ouml;ffnen und sehen direkt, wie dieses hei&szlig;t. Der Code zum Hinzuf&uuml;gen der Eintr&auml;ge mit dem Namen der Kontextmen&uuml;s sieht wie folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>Kontextmenues()\r\n     <span style=\"color:blue;\">Dim <\/span>cbr<span style=\"color:blue;\"> As <\/span>CommandBar\r\n     <span style=\"color:blue;\">Dim <\/span>cbb<span style=\"color:blue;\"> As <\/span>CommandBarButton\r\n     For Each cbr In VBE.CommandBars\r\n         <span style=\"color:blue;\">If <\/span>cbr.Position = msoBarPopup<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">Set<\/span> cbb = Nothing\r\n             On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n             <span style=\"color:blue;\">Set<\/span> cbb = cbr.Controls.Add( _\r\n                 msoControlButton, Temporary:=<span style=\"color:blue;\">True<\/span>)\r\n             <span style=\"color:blue;\">With<\/span> cbb\r\n                 .Caption = \"[\" & cbr.Name & \"]\"\r\n             End <span style=\"color:blue;\">With<\/span>\r\n             <span style=\"color:blue;\">If <\/span>cbb Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n                 <span style=\"color:blue;\">Debug.Print<\/span> \"<span style=\"color:blue;\">Not<\/span> Added: \" & cbr.Name\r\n             <span style=\"color:blue;\">End If<\/span>\r\n             <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Next<\/span> cbr\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Damit k&ouml;nnen wir nun wie in per Rechtsklick einfach den Namen des jeweiligen Kontextmen&uuml;s ermitteln (siehe Bild 8). Diesen verwenden wir wie weiter oben beschrieben, um unsere eigenen Eintr&auml;ge zum Kontextmen&uuml; hinzuzuf&uuml;gen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_421_008.png\" alt=\"Hinzuf&uuml;gen eines Eintrags zum Identifizieren des Namens eines Kontextmen&uuml;s\" width=\"424.6267\" height=\"532.7675\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Hinzuf&uuml;gen eines Eintrags zum Identifizieren des Namens eines Kontextmen&uuml;s<\/span><\/b><\/p>\n<h2>Test und Fehlerermittlung<\/h2>\n<p>Nun haben wir unser eigenes COM-Add-In soweit angepasst, dass wir es testen k&ouml;nnen. Dazu klicken wir auf die <b>Build<\/b>-Schaltfl&auml;che und &ouml;ffnen den VBA-Editor einer der Office-Anwendungen. Genau: Die Funktionen, die wir dem VBA-Editor auf diesem Weg hinzuf&uuml;gen, sind im VBA-Editor aller Office-Anwendungen verf&uuml;gbar.<\/p>\n<h2>Debuggen im Schnelldurchlauf<\/h2>\n<p>Wenn Fehler auftreten sollten, zum Beispiel beim Laden, erscheinen schnell wenig selbsterkl&auml;rende Meldungen wie beispielsweise die aus Bild 9.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_02\/pic_421_009.png\" alt=\"Fehler beim Laden des COM-Add-Ins\" width=\"424.6267\" height=\"207.7405\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Fehler beim Laden des COM-Add-Ins<\/span><\/b><\/p>\n<p>Hier klicken wir zun&auml;chst auf <b>Nein <\/b>und schlie&szlig;en den VBA-Editor und die zugeh&ouml;rige Anwendung wieder.<\/p>\n<p>Dann schauen wir uns speziell den Code an, der durch die Prozedur <b>OnConnection <\/b>ausgel&ouml;st wird, denn dieser wird beim Laden ausgef&uuml;hrt.<\/p>\n<p>Wenn uns dies keine Eingebung liefert, f&uuml;gen wir eine Fehlerbehandlung hinzu. Die erste rudiment&auml;re Version k&ouml;nnte so lauten:<\/p>\n<pre><span style=\"color:blue;\">Sub <\/span>OnConnection(ByVal Application<span style=\"color:blue;\"> As Object<\/span>, _\r\n         ByVal ConnectMode<span style=\"color:blue;\"> As <\/span>ext_ConnectMode, _\r\n         ByVal AddInInst<span style=\"color:blue;\"> As Object<\/span>, _\r\n         ByRef custom<span style=\"color:blue;\"> As Variant<\/span>()) _\r\n         Implements IDTExtensibility2.OnConnection\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> objVBE = Application\r\n     <span style=\"color:blue;\">Set<\/span> objAddIn = AddInInst\r\n     isConnected = <span style=\"color:blue;\">True<\/span>\r\n     CreateToolBar()\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Err.Number = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> Err.Number & <span style=\"color:blue;\">vbCrLf<\/span> & <span style=\"color:blue;\">vbCrLf<\/span> & Err.Description\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Damit sollte eine erste Richtung zu erkennen sein, woher der Fehler kommt. Hilft das nicht, helfen weitere Pr&uuml;fungen:<\/p>\n<ul>\n<li>Stimmen der Klassenname und der in <b>dllRegistration <\/b>f&uuml;r die Konstante <b>AddinClassName <\/b>angegebene Wert &uuml;berein?<\/li>\n<li>Gibt es gegebenenfalls Fehler in den von <b>OnConnection <\/b>aufgerufenen Prozeduren?<\/li>\n<\/ul>\n<p>Wenn sich eine Fehlermeldung zeigt, aber man keine Idee hat, woher dieser kommt, kann man Zeilennummern hinzuf&uuml;gen und sich in der Fehlermeldung zus&auml;tzlich den Wert der nicht dokumentierten Funktion <b>Erl <\/b>ausgeben lassen.<\/p>\n<h2>Gegebenenfalls Benutzeroberfl&auml;che hinzuf&uuml;gen<\/h2>\n<p>Nun kannst Du Deiner Fantasie freien Lauf lassen &#8211; die Prozedur <b>LaunchFunction <\/b>wartet darauf, mit mehr als einer <b>MsgBox<\/b>-Anweisung gef&uuml;llt zu werden. twinBasic bietet neben reinem Code auch die M&ouml;glichkeit, per Benutzeroberfl&auml;che Informationen vom Benutzer abzufragen, die zum Erstellen oder Manipulieren des VBA-Codes im jeweiligen Projekt notwendig sind.<\/p>\n<p>Im Artikel <b>Dateiauswahl-Dialog-Assistent programmieren <\/b>(<b>www.access-im-unternehmen.de\/420<\/b>) zeigen wir, wie wir auf Basis der Vorlage aus diesem Artikel ein COM-Add-In zum Erstellen des Codes f&uuml;r einen Dateiauswahl-Dialog zusammenstellen k&ouml;nnen.<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>VBECOMAddInBasis.twinproj<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/BE394019-A703-4338-B866-2C4EC4E5E2CF\/vbe_421.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wer den VBA-Editor mit eigenen Tools erweitern m&ouml;chte, kommt um die Programmierung von COM-Add-Ins kaum herum &#8211; zumindest nicht, wenn er eine schicke Benutzeroberfl&auml;che und die Integration in Men&uuml;s, Symbolleisten und Kontextmen&uuml;s w&uuml;nscht. In diesem Artikel zeigen wir daher, wie wir die Basis f&uuml;r ein solches COM-Add-In mit twinBASIC programmieren. Ausgehend davon kannst Du direkt loslegen und Deine gew&uuml;nschten Funktionen einbauen &#8211; es sind nur jeweils wenige Anpassung notwendig. Wir erkl&auml;ren Schritt f&uuml;r Schritt, wie die Basis des COM-Add-Ins aufgebaut ist und welche Anpassungen Du vornehmen musst, um ein COM-Add-In f&uuml;r Deine eigenen Anwendungen zu bauen.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[66022024,662024,44000027,44000037],"tags":[],"yst_prominent_words":[],"class_list":["post-55000421","post","type-post","status-publish","format-standard","hentry","category-66022024","category-662024","category-Excel_programmieren","category-VBAEditor_programmieren"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000421","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/comments?post=55000421"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000421\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000421"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000421"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000421"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000421"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}