{"id":55000419,"date":"2024-02-01T00:00:00","date_gmt":"2024-02-24T18:01:22","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=419"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Outlook_Termine_per_COMAddIn_nach_Google","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/Outlook_Termine_per_COMAddIn_nach_Google\/","title":{"rendered":"Outlook: Termine per COM-Add-In nach Google"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg08.met.vgwort.de\/na\/4db3d645ffa1469dbe036ec0944407db\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>In verschiedenen anderen Artikeln haben wir die Techniken vorgestellt, mit denen per VBA wir Termine in den Google Calendar eintragen k&ouml;nnen und auf Ereignisse wie das Anlegen, &Auml;ndern oder L&ouml;schen von Terminen in Outlook reagieren k&ouml;nnen. Wir wollen dies nun alles in einem COM-Add-In zusammenbringen. Das COM-Add-In stellt Ribbon- und Kontextmen&uuml;-Eintr&auml;ge bereit, mit denen man Termine per Mausklick nach Google &uuml;bertragen kann und h&auml;lt Automatismen bereit, die daf&uuml;r sorgen, dass &Auml;nderungen an Terminen wie das Anlegen, Bearbeiten oder L&ouml;schen automatisch in den Google Calendar &uuml;bertragen werden. Damit ist nur noch eine kurze Installation n&ouml;tig, um diese Funktionen in Outlook bereitzustellen.<\/b><\/p>\n<h2>Voraussetzungen und Vorbereitungen<\/h2>\n<p>Die erste Voraussetzung ist, dass Du eine App bei Google erstellt hast. Wie das gelingt, zeigen wir im Artikel <b>Google Calendar programmieren &#8211; Vorbereitungen <\/b>(<b>www.vbentwickler.de\/408<\/b>). Au&szlig;erdem ben&ouml;tigst Du die .NET-DLL, mit der wir das Access-Token f&uuml;r den Zugriff auf den Google Calender holen: <b>Google-Authentifzierung mit OAuth2, Update <\/b>(<b>www.vbentwickler.de\/414<\/b>). Grundlegende Techniken zum automatischen Reagieren auf das Hinzuf&uuml;gen, &Auml;ndern und L&ouml;schen von Outlook-Terminen beschreiben wir in <b>Outlook: Ereignisse f&uuml;r Termine implementieren <\/b>(<b>www.vbentwickler.de\/417<\/b>). Au&szlig;erdem liefert der Artikel <b>Termine von Outlook zum Google Calendar exportieren <\/b>(<b>www.vbentwickler.de\/418<\/b>) Informationen dar&uuml;ber, wie wir Termine von Outlook zum Google Calendar schicken. Schlie&szlig;lich lernst Du Einiges &uuml;ber das Programmieren der Rest-API von Google in den Artikeln <b>Google Calendar per Rest-API programmieren <\/b>(<b>www.vbentwickler.de\/410<\/b>) und <b>Google Calendar per Rest-API programmieren, Teil 2 <\/b>(<b>www.vbentwickler.de\/416<\/b>)<\/p>\n<h2>L&ouml;sung ausprobieren<\/h2>\n<p>Bevor wir beschreiben, wie die L&ouml;sung funktioniert, zeigen wir erst einmal, wie Du sie auf Deinem Rechner installieren und ausprobieren kannst. Dazu f&uuml;hren wir folgende Schritte aus:<\/p>\n<ul>\n<li>.NET-DLL installieren<\/li>\n<li>COM-Add-In f&uuml;r Outlook installieren<\/li>\n<li>Google-App f&uuml;r den Zugriff auf den Kalender erstellen<\/li>\n<li>Outlook starten und die beim Erstellen der Google-App erhaltenen Daten (Client-ID, Client-Secret) in die Optionen eintragen<\/li>\n<li>Authentifizieren<\/li>\n<li>Loslegen!<\/li>\n<\/ul>\n<p>Doch eins nach dem anderen.<\/p>\n<h2>.NET-DLL installieren<\/h2>\n<p>Die .NET-DLL aus dem Artikel <b>Google-Token per DLL holen <\/b>(<b>www.vbentwickler.de\/409<\/b>) findest Du in den Verzeichnissen <b>x64 <\/b>und <b>x86<\/b>. Je nachdem, welche Office-Version Du installiert hast, verwendest Du die aus dem Verzeichnis <b>x64 <\/b>(64-Bit) oder <b>x86<\/b> (32-Bit). Du ben&ouml;tigst das komplette Verzeichnis auf Deinem Rechner. Wo Du dieses speicherst, spielt keine Rolle.<\/p>\n<p>Um die jeweilige Version zu installieren, gehst Du wie folgt vor:<\/p>\n<ul>\n<li>Kopiere den kompletten Pfad zur Datei <b>amvGoogleOAuth2.dll <\/b>in die Zwischenablage.<\/li>\n<li>&Ouml;ffne die Eingabeaufforderung im Administrator-Modus. Dazu gibst Du den Suchbegriff <b>cmd <\/b>in die Windows-Suche ein und klickst mit der rechten Maustaste auf den Eintrag <b>Eingabeaufforderung<\/b>. Hier w&auml;hlst Du den Befehl <b>Als Administrator ausf&uuml;hren&#8230; <\/b>aus.<\/li>\n<li>Navigiere zur Datei <b>Regasm.exe<\/b>. Hier ist wieder die Unterscheidung zwischen Office 32-Bit und Office 64-Bit n&ouml;tig. F&uuml;r 32-Bit navigierst Du beispielsweise zum Verzeichnis <b>C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319<\/b>, f&uuml;r 64-Bit zum Verzeichnis <b>C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319<\/b>. Hier gibst Du f&uuml;r die 32-Bit-Version den folgenden Befehl ein: <b>Regasm &#8220;C:\\&#8230;\\x86\\amvGoogleOAuth2.dll&#8221; \/codebase \/tlb<\/b>. In der Eingabeaufforderung erhalten wir das Ergebnis aus Bild 1.<\/li>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_001.png\" alt=\"Registrieren der .NET-DLL\" width=\"700\" height=\"205,486\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Registrieren der .NET-DLL<\/span><\/b><\/p>\n<\/ul>\n<p>Ob die .NET-DLL korrekt installiert ist, k&ouml;nnen wir in einem beliebigen VBA-Projekt testen. Hier &ouml;ffnen wir den <b>Verweise<\/b>-Dialog (<b>Extras|Verweise<\/b>) und suchen nach dem Eintrag <b>amvGoogleOAuth2<\/b>. Ist der Eintrag wie in Bild 2 vorhanden, k&ouml;nnen wir fortfahren.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_003.png\" alt=\"Pr&uuml;fen, ob die .NET-DLL erfolgreich installiert wurde\" width=\"499,6267\" height=\"393,8742\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Pr&uuml;fen, ob die .NET-DLL erfolgreich installiert wurde<\/span><\/b><\/p>\n<h2>COM-Add-In f&uuml;r Outlook installieren<\/h2>\n<p>Als N&auml;chstes installieren wir das COM-Add-In, dessen Erstellung wir sp&auml;ter in diesem Artikel erl&auml;utern. Das COM-Add-In kommt ebenfalls in zwei Versionen f&uuml;r die 32-Bit- und die 64-Bit-Fassung von Outlook.<\/p>\n<p>Die entsprechenden Dateien hei&szlig;en <b>amvAppointmentsToGoogle_win32.dll <\/b>und <b>amvAppointmentsToGoogle_win64.dll<\/b>. Die Installation ist etwas einfacher:<\/p>\n<ul>\n<li>Als Erstes &ouml;ffnen wir Outlook.<\/li>\n<li>Dann zeigen wir mit dem Ribbonbefehl <b>Datei|Optionen <\/b>den Optionen-Dialog von Outlook an.<\/li>\n<li>Hier wechseln wir zum Bereich <b>Add-Ins<\/b>.<\/li>\n<li>Dort finden wir neben dem Auswahlfeld <b>Verwalten <\/b>mit dem Wert <b>COM-Add-Ins <\/b>die Schaltfl&auml;che <b>Los&#8230;<\/b>, die wir nun bet&auml;tigen.<\/li>\n<li>Damit zeigen wir den Dialog <b>COM-Add-Ins <\/b>an, in dem wir auf die Schaltfl&auml;che <b>Hinzuf&uuml;gen&#8230; <\/b>klicken.<\/li>\n<li>Dies &ouml;ffnet einen Dateiauswahl-Dialog, mit dem wir je nach Office-Version die Datei <b>amvAppointmentsToGoogle_win32.dll <\/b>oder <b>amvAppointmentsToGoogle_win64.dll <\/b>selektieren.<\/li>\n<li>Nach dem Hinzuf&uuml;gen zeigt der Dialog den neuen Eintrag wie in Bild 3 an.<\/li>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_002.png\" alt=\"Installieren des COM-Add-Ins in Outlook\" width=\"649,627\" height=\"271,3763\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Installieren des COM-Add-Ins in Outlook<\/span><\/b><\/p>\n<\/ul>\n<p>Wenn nicht nur dieser Eintrag vorhanden ist, sondern Du unter Datei ganz unten auch noch den Eintrag <b>amvAppointmentToGoogle<\/b>, hat alles funktioniert.<\/p>\n<h2>Google-App f&uuml;r den Zugriff auf den Kalender <\/h2>\n<p>Um fortzufahren, ben&ouml;tigen wir die Client-ID und das Client-Secret einer Google-App. Wie Du diese erh&auml;ltst, haben wir in <b>Google Calendar programmieren &#8211; Vorbereitungen <\/b>(<b>www.vbentwickler.de\/408<\/b>) ausf&uuml;hrlich gezeigt.<\/p>\n<h2>Outlook starten und Optionen eintragen<\/h2>\n<p>Damit k&ouml;nnen wir, falls nicht bereits ge&ouml;ffnet, Outlook starten und die soeben ermittelten Werte in die entsprechenden Optionen eintragen. Dazu w&auml;hlen wir im Ribbon-Tab <b>Datei<\/b> den Eintrag <b>amvAppointmentToGoogle<\/b> aus. Dies zeigt den Bereich aus Bild 4 an.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_004.png\" alt=\"Die Optionen des COM-Add-Ins amv-Appoint-ment-ToGoogle\" width=\"700\" height=\"390,6975\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Die Optionen des COM-Add-Ins amv-Appoint-ment-ToGoogle<\/span><\/b><\/p>\n<p>Hier sehen wir oben die beiden Eigenschaften <b>Client-ID <\/b>und <b>Client-Secret<\/b>, die noch leer sein d&uuml;rften. Hier tr&auml;gst Du die beim Erstellen der Google-App erhaltenen Informationen ein.<\/p>\n<h2>Authentifizieren<\/h2>\n<p>Danach klicken wir auf die Schaltfl&auml;che <b>Token aktualisieren<\/b>, um erstmalig das Access-Token zu ermitteln, das wir f&uuml;r den Zugriff auf den Google Calendar im Kontext eines Benutzers ben&ouml;tigen.<\/p>\n<p>F&uuml;r diesen Schritt ben&ouml;tigen wir die .NET-DLL. Die darin enthaltenen Funktionen konnten wir nicht mit vertretbarem Aufwand in einer DLL auf Basis von VB6\/twinBASIC erstellen geschweige denn mit VBA realisieren, daher haben wir hier .NET gew&auml;hlt.<\/p>\n<p>Danach &ouml;ffnet sich ein Webbrowser und zeigt den Dialog an, mit dem wir angeben wollen, welches Konto wir als Kontext f&uuml;r den Zugriff auf den Google Calendar nutzen wollen (siehe Bild 5).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_005.png\" alt=\"Konto f&uuml;r das Access-Token authentifizieren\" width=\"499,6267\" height=\"380,2469\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Konto f&uuml;r das Access-Token authentifizieren<\/span><\/b><\/p>\n<p>Durch die Anmeldung mit diesem Konto erfolgt die Authentifizierung und die .NET-DLL liefert uns das Access-Token, ein Refresh-Token und die Zeit, wie lange das Token g&uuml;ltig ist und f&uuml;r den Zugriff auf den Google Calendar &uuml;ber die Rest-API erfolgen kann. Das Access-Token wird daraufhin gleich im entsprechenden Feld der Optionen angezeigt.<\/p>\n<p>Wir k&ouml;nnen uns auch gleich die E-Mail-Adresse merken, die wir f&uuml;r die Authentifizierung verwendet haben. Diese tragen wir nun in das Feld <b>Calendar-ID <\/b>der Optionen des COM-Add-Ins ein. Alle Daten, die Du in den Optionen siehst, werden in der Registry gespeichert &#8211; mehr dazu sp&auml;ter.<\/p>\n<h2>COM-Add-In anwenden<\/h2>\n<p>Danach k&ouml;nnen wir das COM-Add-In in Outlook ausprobieren. Wir haben folgende Funktionen implementiert:<\/p>\n<ul>\n<li>Manuelles &Uuml;bertragen eines Termins zum Google Calendar per Kontextmen&uuml; oder Ribbon-Eintrag<\/li>\n<li>Manuelles L&ouml;schen eines Termins per Kontextmen&uuml; oder Ribboneintrag<\/li>\n<li>Automatisches Anlegen eines neuen Termins im Google Calendar, wenn ein neuer Termin im Outlook erstellt wird<\/li>\n<li>Automatisches Aktualisieren eines Termins im Google Calendar, wenn dieser in Outlook ge&auml;ndert wurde<\/li>\n<li>Automatisches L&ouml;schen beziehungsweise Setzen von Google-Terminen auf den Status <b>Cancelled<\/b>, wenn der entsprechende Outlook-Termin gel&ouml;scht wurde<\/li>\n<\/ul>\n<p>Wenn wir beispielsweise einen Termin aktivieren, zeigt das Ribbon-Tab <b>Termin <\/b>im Bereich <b>Google Calendar <\/b>zwei Befehle an. Mit <b>In Google Calendar &uuml;bertragen <\/b>schicken wir den Termin zum Google Calendar.<\/p>\n<p>Mit <b>Aus Google Calendar l&ouml;schen <\/b>entfernen wir den Termin wieder beziehungsweise setzen seinen Status auf <b>Cancelled<\/b> (siehe Bild 6).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_006.png\" alt=\"Funktionen zum manuellen &Uuml;bertragen und L&ouml;schen von Terminen\" width=\"700\" height=\"369,4247\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Funktionen zum manuellen &Uuml;bertragen und L&ouml;schen von Terminen<\/span><\/b><\/p>\n<p>Die gleichen Befehle tauchen nochmals auf, wenn wir das Kontextmen&uuml; eines Termins &ouml;ffnen und dort das Untermen&uuml; <b>Google Calendar &#8230; <\/b>&ouml;ffnen.<\/p>\n<h2>Automatische Synchronisierung aktivieren<\/h2>\n<p>Damit Termindaten automatisch zum Google Calendar &uuml;bertragen werden, sobald ein Termin angelegt, ge&auml;ndert oder gel&ouml;scht wird, m&uuml;ssen wir einige Optionen aktivieren. Diese finden wir im unteren Bereich der Optionen von <b>amvAppointmentToGoogle<\/b>.<\/p>\n<p>Hier k&ouml;nnen wir festlegen, ob neu angelegte Termine, ge&auml;nderte Termine oder gel&ouml;schte Termine mit dem Google Calendar synchronisiert werden sollen (siehe Bild 7). Mit der Option <b>Mit Google synchronisierte Termine in diese Kategorie: <\/b>k&ouml;nnen wir festlegen, dass Termine in Outlook, die nach Google &uuml;bertragen wurden, automatisch eine bestimmte Kategorie erhalten. Den Namen dieser Kategorie geben wir im Textfeld rechts daneben an. Wichtig: Die Kategorie muss vorhanden sein, sonst wird diese nicht zugeordnet.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_007.png\" alt=\"Aktivieren der automatischen Synchronisierung\" width=\"649,627\" height=\"302,7386\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Aktivieren der automatischen Synchronisierung<\/span><\/b><\/p>\n<h2>Programmieren des COM-Add-Ins<\/h2>\n<p>Das COM-Add-In haben wir mit twinBASIC programmiert, der neuen Entwicklungsumgebung, die sich als Nachfolger von VB6 anschickt und &uuml;ber die wir bereits in einigen Artikeln geschrieben haben &#8211; mehr liefert die Suchfunktion mit dem Begriff twinBASIC auf <b>https:\/\/www.vbentwickler.de<\/b>.<\/p>\n<h2>Anlegen des Projekts und Optionen einstellen<\/h2>\n<p>Wir legen ein neues twinBASIC-Projekt an und stellen unter Settings den Projektnamen, die Beschreibung und den Anwendungstitel alle auf den Text <b>amvAppointmentsToGoogle <\/b>ein.<\/p>\n<p>Au&szlig;erdem f&uuml;gen wir unter <b>Library References <\/b>noch einige zus&auml;tzliche Eintr&auml;ge hinzu (siehe Bild 8). Vor allem ist hier der Verweis auf die .NET-DLL <b>amvGoogleOAuth2 <\/b>wichtig.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_008.png\" alt=\"Verweise des twinBASIC-Projekts\" width=\"599,6265\" height=\"423,5735\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Verweise des twinBASIC-Projekts<\/span><\/b><\/p>\n<h2>Registrierung auf Outlook anpassen<\/h2>\n<p>Damit das COM-Add-In beim Start von Outlook ebenfalls gestartet wird, nehmen wir am Modul <b>DLLRegistration <\/b>der COM-Add-In-Vorlage von twinBASIC einige &Auml;nderungen vor.<\/p>\n<p>F&uuml;r die Konstante <b>AddinClassName <\/b>tragen wir <b>amvAppointmentsToGoogle <\/b>ein. Den Zielpfad in der Registry passen wir auf Outlook an:<\/p>\n<pre>Const RootRegistryFolder<span style=\"color:blue;\"> As String<\/span> = \"HKCU\\SOFTWARE\\Microsoft\\Office\\Outlook\\Addins\\\" & AddinQualifiedClassName & \"\\\"<\/pre>\n<p>Die restlichen Anpassungen kannst Du im Modul <b>DLLRegistration<\/b> anschauen.<\/p>\n<p>Die Programmierung des Ribbons und der Kontextmen&uuml;s von Outlook haben wir bereits in den folgenden Artikel ausf&uuml;hrlich erl&auml;utert:<\/p>\n<ul>\n<li><b>Outlook: Ribbon per COM-Add-In anpassen <\/b>(<b>www.vbentwickler.de\/376<\/b>)<\/li>\n<li><b>Outlook: Kontextmen&uuml;s anpassen <\/b>(<b>www.vbentwickler.de\/369<\/b>)<\/li>\n<li><b>Outlook: Anhang speichern per Kontextmen&uuml; <\/b>(<b>www.vbentwickler.de\/375<\/b>)<\/li>\n<\/ul>\n<p>Daher gehen wir hier etwas knapper auf die verwendeten Techniken ein und beschreiben diese einmal etwas anders &#8211; n&auml;mlich nach dem Zeitpunkt des Aufrufs.<\/p>\n<h2>Start des COM-Add-Ins<\/h2>\n<p>Sobald das COM-Add-In einmal erstellt und registriert ist, stehen in der Registry einige Eintr&auml;ge, die Outlook beim Start ausliest.<\/p>\n<p>Dadurch wird das COM-Add-In direkt beim Start von Outlook ebenfalls geladen.<\/p>\n<p>Die dazu wichtigsten Funktionen sind in der Hauptklasse des COM-Add-Ins namens <b>amvAppointmentsToGoogle <\/b>enthalten.<\/p>\n<p>Beim Starten geschehen zwei Dinge:<\/p>\n<ul>\n<li>Die Ereignisprozedur <b>OnConnection <\/b>der Schnittstelle <b>IDTExtensibility2 <\/b>wird ausgel&ouml;st. Hier f&uuml;hren wir initiale Schritte durch.<\/li>\n<li>Die Funktion <b>GetCustom <\/b>der Schnittstelle <b>IRibbonExtensibility <\/b>wird aufgerufen. Diese liefert die Ribbondefinition f&uuml;r das COM-Add-In an Outlook.<\/li>\n<\/ul>\n<h2>Wichtige Variablen initialisieren<\/h2>\n<p>Wir ben&ouml;tigen verschiedene Variablen, die beim Starten des COM-Add-Ins gef&uuml;llt werden. Eine davon muss anwendungsweit verf&uuml;gbar sein und nimmt den Verweis auf die Outlook-Anwendung auf, die das COM-Add-In geladen hat.<\/p>\n<p>Daher deklarieren wir diese als Public im Modul <b>mdlOutlook<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Public <\/span>objOutlook<span style=\"color:blue;\"> As <\/span>Outlook.Application<\/pre>\n<p>Drei weitere Variablen ben&ouml;tigen wir nur in der Klasse <b>amvAppointmentsToGoogle<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>objRibbon<span style=\"color:blue;\"> As <\/span>IRibbonUI\r\n<span style=\"color:blue;\">Private <\/span>WithEvents objCalendar<span style=\"color:blue;\"> As <\/span>Outlook.Folder\r\n<span style=\"color:blue;\">Private <\/span>WithEvents objCalendarItems<span style=\"color:blue;\"> As <\/span>Outlook.Items<\/pre>\n<ul>\n<li>Die Variable <b>objRibbon <\/b>nimmt einen Verweis auf die benutzerdefinierte Ribbondefinition des COM-Add-Ins auf.<\/li>\n<li><b>objCalendar <\/b>referenziert den Ordner mit den Kalendereintr&auml;gen. Diese Variable wird mit dem Schl&uuml;sselwort <b>WithEvents <\/b>deklariert, damit wir die Ereignisse des <b>Folder<\/b>-Elements implementieren k&ouml;nnen.<\/li>\n<li><b>objCalendarItems <\/b>referenziert die <b>Items<\/b>-Auflistung des Kalender-Ordners. Auch diese Objektvariable deklarieren wir mit <b>WithEvents<\/b>.<\/li>\n<\/ul>\n<p>Die Initialisierung der Variablen erfolgt in der beim Start von Outlook ausgel&ouml;sten Ereignisprozedur <b>OnConnection<\/b> (siehe Listing 1).<\/p>\n<pre><span style=\"color:blue;\">Sub <\/span>OnConnection(ByVal Application<span style=\"color:blue;\"> As Object<\/span>, ByVal ConnectMode<span style=\"color:blue;\"> As <\/span>ext_ConnectMode, ByVal AddInInst<span style=\"color:blue;\"> As Object<\/span>, _\r\n         ByRef custom<span style=\"color:blue;\"> As Variant<\/span>()) Implements IDTExtensibility2.OnConnection\r\n     <span style=\"color:blue;\">Set<\/span> objOutlook = Application\r\n     <span style=\"color:blue;\">Set<\/span> objCalendar = objOutlook.GetNamespace(\"MAPI\").GetDefaultFolder(olFolderCalendar)\r\n     <span style=\"color:blue;\">Set<\/span> objCalendarItems = objCalendar.Items\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Die Funktion GetCustomUI stellt eine Ribbondefinition zusammen und gibt diese zur&uuml;ck.<\/span><\/b><\/p>\n<p>Diese erh&auml;lt mit ihren Parametern zum Beispiel einen Verweis auf die aufrufende Anwendung. Diesen speichern wir in der Objektvariablen <b>objOutlook<\/b>.<\/p>\n<p>Den <b>Kalender<\/b>-Ordner f&uuml;r die Variable <b>objCalendar <\/b>ermitteln wir &uuml;ber die Funktion <b>GetDefaultFolder <\/b>des MAPI-Namespaces und in <b>objCalendarItems <\/b>speichern wir die <b>Items<\/b>-Auflistung des gleichen Ordners.<\/p>\n<h2>Laden des Ribbons<\/h2>\n<p>Damit unsere Ribbondefinition beim Laden eingelesen wird, ruft das COM-Add-In beim Starten automatisch die Funktion <b>GetCustomUI <\/b>auf, welche die Ribbondefinition zusammenstellen.<\/p>\n<p>Diese besteht unterhalb der Elemente <b>customUI <\/b>und <b>ribbon <\/b>aus mehreren Teilen, die genau so angeordnet werden m&uuml;ssen, wie es hier der Fall ist:<\/p>\n<ul>\n<li><b>contextualTabs<\/b>: Passt das kontextabh&auml;ngige Ribbon-Tab mit der Beschriftung <b>Termine <\/b>an und f&uuml;gt zwei Schaltfl&auml;chen zum &Uuml;bertragen eines Termins nach Google und zum L&ouml;schen bestehender Termine aus dem Google Calendar hinzu.<\/li>\n<li><b>backstage<\/b>: Erweitert den Backstage-Bereich, den wir &uuml;ber das Ribbon-Tab Datei anzeigen, um einen Eintrag namens amvAppointmentsToGoogle. Klickt man darauf, erscheinen einige Optionen f&uuml;r das COM-Add-In.<\/li>\n<li><b>contextMenu<\/b>: Passt das Kontextmen&uuml; von Terminen in der Kalender&uuml;bersicht an und f&uuml;gt ein Untermen&uuml; mit den beiden Befehlen zum &Uuml;bertragen von Terminen nach Google und zum L&ouml;schen von dort vorhandenen Terminen hinzu.<\/li>\n<\/ul>\n<p>Eine komplette Beschreibung der Ribbondefinition w&uuml;rde den Rahmen sprengen. Einige Feinheiten wollen wir jedoch dokumentieren.<\/p>\n<h2>Aufruf der Ribbon-Befehle<\/h2>\n<p>Die Schaltfl&auml;chen, die wir in den drei Ribbonerweiterungen definieren, enthalten jeweils das Attribut <b>onAction<\/b>.<\/p>\n<p>Ein Klick diese Schaltfl&auml;chen l&ouml;st die daf&uuml;r hinterlegte VBA-Prozedur <b>onAction <\/b>aus, die wie folgt aussieht:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>OnAction(control<span style=\"color:blue;\"> As <\/span>IRibbonControl)\r\n     Select Case control.Id\r\n         <span style=\"color:blue;\">Case <\/span>\"btnRefreshToken\"\r\n             GetTokens\r\n             objRibbon.Invalidate()\r\n         <span style=\"color:blue;\">Case <\/span>\"btnInGoogleCalendarUebertragen\", _\r\n                 \"btnInGoogleCalendarUebertragen_Shortcut\"\r\n             <span style=\"color:blue;\">Call<\/span> InsertEventInGoogleCalendar\r\n         <span style=\"color:blue;\">Case <\/span>\"btnAusGoogleCalendarLoeschen\", _\r\n                 \"btnAusGoogleCalendarLoeschen_Shortcut\"\r\n             <span style=\"color:blue;\">Call<\/span> DeleteEventFromGoogleCalendar\r\n         <span style=\"color:blue;\">Case Else<\/span>\r\n             <span style=\"color:blue;\">MsgBox<\/span> \"OnAction nicht behandelt: \" & control.Id\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die <b>Select Case<\/b>-Bedingung pr&uuml;ft den Wert der Eigenschaft <b>Id <\/b>des Parameters <b>Control<\/b>, die den Namen des Steuerelements liefert. Dar&uuml;ber rufen wir verschiedene Prozeduren auf. Die beiden Schaltfl&auml;chen <b>btnInGoogleCalendarUebertragen<\/b> und <b>btnInGoogleCalendarUebertragen_Shortcut <\/b>sorgen f&uuml;r den Aufruf der Prozedur <b>InsertEventInGoogleCalendar<\/b>, die beiden Schaltfl&auml;chen <b>btnAusGoogleCalendarLoeschen <\/b>und <b>btnAusGoogleCalendarLoeschen_Shortcut <\/b>die Prozedur <b>DeleteEventFromGoogleCalendar<\/b>.<\/p>\n<p>Diese Schaltfl&auml;chen finden wir bei aktiviertem Termin im Ribbon (siehe Bild 9) und im Kontextmen&uuml; eines Termins.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_009.png\" alt=\"Benutzerdefinierte Ribbonbefehle f&uuml;r einen Termin\" width=\"499,6267\" height=\"189,6394\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Benutzerdefinierte Ribbonbefehle f&uuml;r einen Termin<\/span><\/b><\/p>\n<p>Die Schaltfl&auml;che <b>btnRefreshToken<\/b> wiederum befindet sich im Backstage-Bereich. Sie dient der Ermittlung des Access-Tokens f&uuml;r den Rest-API-Zugriff auf den Google Calendar &uuml;ber die Funktion <b>GetTokens<\/b> (siehe Listing 2).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>GetTokens()<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strClientID<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strClientSecret<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strRefreshToken<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strAccessToken<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngExpiresInSeconds<span style=\"color:blue;\"> As Long<\/span>\r\n     strClientID = GetAppSetting(\"ClientID\")\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strClientID) = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Keine ClientID in der Registry gefunden. Gebe diesen Wert in den Optionen ein.\", _\r\n             vbOKOnly + vbExclamation, \"ClientID fehlt\"\r\n         <span style=\"color:blue;\">Exit Function<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     strClientSecret = GetAppSetting(\"ClientSecret\")\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strClientSecret) = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Kein ClientSecret in der Registry gefunden. Gebe diesen Wert in den Optionen ein.\", _\r\n             vbOKOnly + vbExclamation, \"ClientSecret fehlt\"\r\n         <span style=\"color:blue;\">Exit Function<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     Kill Environ(\"AppData\") & \"\\Google.Apis.Auth\\Google.Apis.Auth.OAuth2.Responses.TokenResponse-user\"\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n     <span style=\"color:blue;\">If <\/span>GetTokensDLL(strClientID, strClientSecret, strRefreshToken, strAccessToken, lngExpiresInSeconds)<span style=\"color:blue;\"> Then<\/span>\r\n         SaveAppSetting \"RefreshToken\", strRefreshToken\r\n         SaveAppSetting \"AccessToken\", strAccessToken\r\n         SaveAppSetting \"TokenExpiresAt\", DateAdd(\"s\", lngExpiresInSeconds, Now)\r\n         GetTokens = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Die Funktion GetTokens startet den Prozess zur Ermittlung des Access-Tokens.<\/span><\/b><\/p>\n<h2>Access-Token holen<\/h2>\n<p>Wie weiter oben schon beschrieben, ist das Holen des Access-Tokens ein wichtiger Schritt zum Arbeiten mit der Rest-API des Google Calendars.<\/p>\n<p>Daher schauen wir uns dies genauer an. Der Vorgang wird initial einmal &uuml;ber die Schaltfl&auml;che <b>Token aktualisieren <\/b>ausgel&ouml;st, aber auch bei erneuten Zugriffen auf die Rest-API.<\/p>\n<h2>Speichern der Optionen in der Registry<\/h2>\n<p>F&uuml;r das Verst&auml;ndnis ist es wichtig, dass wir die Optionen, die im Backstage-Bereich des Ribbons unter <b>amvAppointmentsToGoogle <\/b>angezeigt werden, in der Registry speichern (siehe Bild 10).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_010.png\" alt=\"Einstellungen des COM-Add-Ins in der Registry\" width=\"649,627\" height=\"292,9048\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: Einstellungen des COM-Add-Ins in der Registry<\/span><\/b><\/p>\n<p>Zum Einlesen und zum Speichern nutzen wir zwei Routinen, die wir im Artikel <b>Anwendungsdaten in der Registry <\/b>(<b>www.vbentwickler.de\/411<\/b>) vorgestellt haben. Die Prozedur <b>SaveAppSetting <\/b>speichert einen Wert in einem vorgegebenen Pfad der Registry, die Funktion <b>GetAppSetting <\/b>liest diese wieder aus. Beide Routinen befinden sich im Modul <b>mdlRegistry<\/b>.<\/p>\n<h2>Client-ID und Client-Secret pr&uuml;fen<\/h2>\n<p>Die Funktion <b>GetTokens <\/b>pr&uuml;ft nun als Erstes, ob bereits Client-ID und Client-Secret in den Optionen hinterlegt wurden. Falls nicht, erscheint jeweils eine entsprechende Meldung und die Funktion wird beendet. Liegen beide Informationen vor, l&ouml;scht die Prozedur eine Datei, die eventuell Probleme beim Versuch macht, das Access-Token erneut freizugeben.<\/p>\n<p>Dann ruft sie die Prozedur <b>GetTokensDLL <\/b>auf und &uuml;bergibt Client-ID und Client-Secret. Au&szlig;erdem verwendet sie drei R&uuml;ckgabeparameter f&uuml;r das Refresh-Token, das Access-Token und die Anzahl der Sekunden, f&uuml;r die das Access-Token ab jetzt g&uuml;ltig ist.<\/p>\n<p>War der Aufruf erfolgreich, speichert die Funktion die von <b>GetTokensDLL <\/b>zur&uuml;ckgelieferten Informationen in der Registry und gibt den Wert <b>True <\/b>zur&uuml;ck. Die Funktion <b>GetTokensDLL <\/b>haben wir bereits im Artikel <b>Google Calendar per Rest-API programmieren <\/b>(<b>www.vbentwickler.de\/410<\/b>) beschrieben.<\/p>\n<p>Wenn die Funktion <b>GetTokens <\/b>&uuml;ber die Backstage-Schaltfl&auml;che <b>Token aktualisieren <\/b>gestartet wurde, wird anschlie&szlig;end noch die folgende Anweisung aufgerufen:<\/p>\n<pre>objRibbon.Invalidate()<\/pre>\n<p>Dies sorgt daf&uuml;r, dass die Elemente des Ribbons, die mit Callback-Funktionen ausgestattet sind, aktualisiert werden. Dazu geh&ouml;ren alle Elemente des Backstage-Bereichs, die Daten aus der Registry anzeigen.<\/p>\n<h2>Backstage mit Daten f&uuml;llen<\/h2>\n<p>In der Ribbondefinition f&uuml;r den Backstage-Bereich finden wir beispielsweise solche <b>editBox<\/b>-Steuerelemente:<\/p>\n<pre>&lt;editBox id=\"\"txtClientID\"\" getText=\"\"getText\"\" sizeString=\"\"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\"\" onChange=\"\"onChange\"\"\/&gt;\"<\/pre>\n<p>Dies betrachten wir stellvertretend f&uuml;r alle <b>editBox<\/b>-Elemente. Wichtig sind hier die beiden Attribute <b>getText <\/b>und <b>onChange<\/b>. <b>getText <\/b>wird beim Anzeigen des Backstage-Bereichs oder beim Aufrufen der <b>Invalidate<\/b>-Methode des Objekts <b>objRibbon <\/b>ausgel&ouml;st und erm&ouml;glicht es uns, das <b>editBox<\/b>-Elements zur Laufzeit zu f&uuml;llen.<\/p>\n<p>Die Funktion <b>GetText <\/b>verarbeitet in einer <b>Select Case<\/b>-Bedingung den Namen des aufrufenden Steuerelements und ermittelt den entsprechenden Wert aus der Registry, sodass dieser von der Funktion zur&uuml;ckgegeben und im <b>editBox<\/b>-Element angezeigt werden kann (siehe Listing 3).<\/p>\n<pre><span style=\"color:blue;\">Function <\/span>GetText(control<span style=\"color:blue;\"> As <\/span>IRibbonControl)<span style=\"color:blue;\"> As String<\/span>\r\n     Select Case control.Id\r\n         <span style=\"color:blue;\">Case <\/span>\"txtClientID\"\r\n             Return GetAppSetting(\"ClientID\")\r\n         <span style=\"color:blue;\">Case <\/span>\"txtClientSecret\"\r\n             Return GetAppSetting(\"ClientSecret\")\r\n         <span style=\"color:blue;\">Case <\/span>\"txtCalendarID\"\r\n             Return GetAppSetting(\"CalendarID\")\r\n         <span style=\"color:blue;\">Case <\/span>\"txtTokenExpiresAt\"\r\n             Return GetAppSetting(\"TokenExpiresAt\")\r\n         <span style=\"color:blue;\">Case <\/span>\"txtAccessToken\"\r\n             Return GetAppSetting(\"AccessToken\")\r\n         <span style=\"color:blue;\">Case <\/span>\"txtSynchronisierteInDieseKategorie\"\r\n             Return GetAppSetting(\"CategoryForSynchronizedEvents\")\r\n         <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Die Funktion GetText liest die Texte f&uuml;r die editBox-Elemente ein.<\/span><\/b><\/p>\n<p>Die Funktion <b>onChange<\/b> sieht wie in Listing 4 aus. Sie erh&auml;lt neben dem Verweis auf das aufrufende Steuerelement den aktuellen Inhalt des <b>editBox<\/b>-Elements als Parameter.<\/p>\n<pre><span style=\"color:blue;\">Sub <\/span>OnChange(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, text<span style=\"color:blue;\"> As String<\/span>)\r\n     Select Case control.Id\r\n         <span style=\"color:blue;\">Case <\/span>\"txtClientID\"\r\n             SaveAppSetting \"ClientID\", text\r\n         <span style=\"color:blue;\">Case <\/span>\"txtClientSecret\"\r\n             SaveAppSetting \"ClientSecret\", text\r\n         <span style=\"color:blue;\">Case <\/span>\"txtCalendarID\"\r\n             SaveAppSetting \"CalendarID\", text\r\n         <span style=\"color:blue;\">Case <\/span>\"txtTokenExpiresAt\"\r\n             SaveAppSetting \"TokenExpiresAt\", text\r\n         <span style=\"color:blue;\">Case <\/span>\"txtAccessToken\"\r\n             SaveAppSetting \"AccessToken\", text\r\n         <span style=\"color:blue;\">Case <\/span>\"txtSynchronisierteInDieseKategorie\"\r\n             SaveAppSetting \"CategoryForSynchronizedEvents\", text\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Die Funktion onChange aktualisiert die Werte in der Registry.<\/span><\/b><\/p>\n<p>Sie verwendet ebenfalls eine <b>Select Case<\/b>-Bedingung, in der die Werte des <b>editBox<\/b>-Elements an der entsprechenden Stelle im der Registry eingetragen werden.<\/p>\n<h2>checkBox-Elemente lesen und schreiben<\/h2>\n<p>F&uuml;r die <b>checkBox<\/b>-Elemente schauen wir uns stellvertretend die Definition des folgenden Elements an:<\/p>\n<pre>&lt;checkBox id=\"\"chkBeimAnlegenAutomatischUebertragen\"\" getPressed=\"\"getPressed\"\" onAction=\"\"onActionCheckBox\"\"\/&gt;\"<\/pre>\n<p>Hier finden wir zwei andere Callback-Attribute, n&auml;mlich <b>getPressed <\/b>und <b>onAction<\/b>.<\/p>\n<p>Die Callback-Funktion <b>getPressed <\/b>ermittelt den gew&uuml;nschten Zustand f&uuml;r das <b>checkBox<\/b>-Element, also entweder aktiviert oder nicht aktiviert (siehe Listing 5). Wie bei den <b>editBox<\/b>-Elementen verarbeiten wir die verschiedenen aufrufenden Steuerelemente in einer <b>Select Case<\/b>-Bedingung.<\/p>\n<pre><span style=\"color:blue;\">Function <\/span>GetPressed(control<span style=\"color:blue;\"> As <\/span>IRibbonControl)<span style=\"color:blue;\"> As Boolean<\/span>\r\n     Select Case control.Id\r\n         <span style=\"color:blue;\">Case <\/span>\"chkBeimAnlegenAutomatischUebertragen\"\r\n             Return GetAppSetting(\"CreateGoogleEventOnAdd\", <span style=\"color:blue;\">False<\/span>)\r\n         <span style=\"color:blue;\">Case <\/span>\"chkBeimAendernAutomatischUebertragen\"\r\n             Return GetAppSetting(\"CreateGoogleEventOnUpdate\", <span style=\"color:blue;\">False<\/span>)\r\n         <span style=\"color:blue;\">Case <\/span>\"chkBeimLloeschenAuchInGoogleCalendarLoeschen\"\r\n             Return GetAppSetting(\"DeleteGoogleEventOnDelete\", <span style=\"color:blue;\">False<\/span>)\r\n         <span style=\"color:blue;\">Case <\/span>\"chkSynchronisierteInKategorie\"\r\n             Return GetAppSetting(\"SetCategoryForSynchronizedEvents\", <span style=\"color:blue;\">False<\/span>)\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Die Funktion GetPressed ermittelt den Status eines checkBox-Elements.<\/span><\/b><\/p>\n<p>Darin ermitteln wir jeweils den Wert der entsprechenden Einstellung aus der Registry und geben diesen als Funktionswert zur&uuml;ck, sodass das <b>checkBox<\/b>-Element entweder einen Haken anzeigt oder leer ist.<\/p>\n<p>Die umgekehrte Funktion erreichen wir mit der f&uuml;r das Attribut <b>onAction <\/b>hinterlegten Prozedur <b>onActionCheckBox<\/b>. Warum &uuml;berhaupt eine eigene Prozedur f&uuml;r die <b>checkBox<\/b>-Elemente, wenn wir doch schon eine <b>onAction<\/b>-Prozedur f&uuml;r die <b>button<\/b>-Elemente haben? Weil die f&uuml;r das <b>checkBox<\/b>-Element eine andere Signatur aufweist, denn sie hat noch einen weiteren Parameter, der beim Anklicken den aktuellen Zustand des <b>checkBox<\/b>-Elements &uuml;bergibt.<\/p>\n<p>Diesen tr&auml;gt die Prozedur <b>onActionCheckBox <\/b>aus Listing 6 in die jeweiligen Optionen in der Registry ein.<\/p>\n<pre><span style=\"color:blue;\">Sub <\/span>onActionCheckBox(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, pressed<span style=\"color:blue;\"> As Boolean<\/span>)\r\n     Select Case control.Id\r\n         <span style=\"color:blue;\">Case <\/span>\"chkBeimAnlegenAutomatischUebertragen\"\r\n             SaveAppSetting \"CreateGoogleEventOnAdd\", CStr(pressed)\r\n         <span style=\"color:blue;\">Case <\/span>\"chkSynchronisierteInKategorie\"\r\n             SaveAppSetting \"SetCategoryForSynchronizedEvents\", CStr(pressed)\r\n         <span style=\"color:blue;\">Case <\/span>\"chkBeimAendernAutomatischUebertragen\"\r\n             SaveAppSetting \"CreateGoogleEventOnUpdate\", CStr(pressed)\r\n         <span style=\"color:blue;\">Case <\/span>\"chkBeimLloeschenAuchInGoogleCalendarLoeschen\"\r\n             SaveAppSetting \"DeleteGoogleEventOnDelete\", CStr(pressed)\r\n         <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: onActionCheckBox feuert beim Anklicken eines checkBox.<\/span><\/b><\/p>\n<h2>dropBox-Element mit Kategorien f&uuml;llen<\/h2>\n<p>Spannend ist noch das <b>dropBox<\/b>-Element mit der folgenden Definition:<\/p>\n<pre>&lt;comboBox id=\"cboCategories\" onChange=\"onChangeComboBox\" getItemCount=\"getItemCount\" getItemID=\"getItemID\" getItemLabel=\"getItemLabel\" getText=\"getTextComboBox\"\/&gt;<\/pre>\n<p>Hier finden wir gleich f&uuml;nf Callback-Funktionen, die in Listing 7 abgebildet sind. Die Funktion <b>GetItemCount <\/b>z&auml;hlt die anzuzeigenden Elemente. Diese Anzahl holt sie mit der <b>Count<\/b>-Methode der <b>Categories<\/b>-Auflistung des Outlook-MAPI-Namespaces und gibt sie dann zur&uuml;ck. Die Funktion <b>GetItemID <\/b>ist eigentlich unn&ouml;tig, da wir die ID nicht ben&ouml;tigen. Wir f&uuml;llen diese daher einfach mit dem &uuml;bergebenen <b>Index<\/b>-Wert.<\/p>\n<pre><span style=\"color:blue;\">Function <\/span>GetItemCount(control<span style=\"color:blue;\"> As <\/span>IRibbonControl)<span style=\"color:blue;\"> As Integer<\/span>\r\n     Return objOutlook.GetNamespace(\"MAPI\").Categories.Count()\r\n<span style=\"color:blue;\">End Function<\/span>\r\n<span style=\"color:blue;\">Function <\/span>GetItemID(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, index<span style=\"color:blue;\"> As Integer<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n     Return index\r\n<span style=\"color:blue;\">End Function<\/span>\r\n<span style=\"color:blue;\">Function <\/span>GetItemLabel(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, index<span style=\"color:blue;\"> As Integer<\/span>) _\r\n        <span style=\"color:blue;\"> As String<\/span>\r\n     Return objOutlook.GetNamespace(\"MAPI\").Categories(index + 1).Name\r\n<span style=\"color:blue;\">End Function<\/span>\r\n<span style=\"color:blue;\">Function <\/span>GetTextComboBox(control<span style=\"color:blue;\"> As <\/span>IRibbonControl)<span style=\"color:blue;\"> As String<\/span>\r\n     Return GetAppSetting(\"CategoryForSynchronizedEvents\")\r\n<span style=\"color:blue;\">End Function<\/span>\r\n<span style=\"color:blue;\">Sub <\/span>OnChangeComboBox(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, text<span style=\"color:blue;\"> As String<\/span>)\r\n     SaveAppSetting \"CategoryForSynchronizedEvents\", text\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 7: Routinen zum F&uuml;llen der ComboBox<\/span><\/b><\/p>\n<p>Wichtiger ist die Funktion <b>GetItemLabel<\/b>. Diese liest die Bezeichnung der jeweiligen Kategorie von Outlook aus der Auflistung <b>Categories <\/b>aus und gibt diese zur&uuml;ck.<\/p>\n<p>Die Funktion <b>GetTextComboBox <\/b>holt den Wert, der zuletzt ausgew&auml;hlt und in der Registry gespeichert wurde, aus der Registry. Schlie&szlig;lich fehlt noch die Prozedur <b>OnChangeComboBox<\/b>. Diese wird ausgel&ouml;st, wenn der Benutzer einen neuen Eintrag ausw&auml;hlt. Die Funktion <b>SaveAppSetting <\/b>schreibt diesen in die Registry, wo er sp&auml;ter wieder ausgelesen werden kann.<\/p>\n<h2>Termin nach Google &uuml;bertragen<\/h2>\n<p>Einen Termin &uuml;bertr&auml;gt das COM-Add-In in den Google Calendar, wenn wir einen Termin markieren und dann den Ribbon-Eintrag oder den Kontextmen&uuml;-Eintrag aufrufen.<\/p>\n<p>Dies ruft die Prozedur <b>InsertEventInGoogleCalendar <\/b>auf (siehe Listing 8). Diese ermittelt mit der Hilfsfunktion <b>GetSelectedAppointmentItem <\/b>(siehe <b>Outlook: Kalender und Termine programmieren <\/b>(<b>www.vbentwickler.de\/415<\/b>)) den aktuell markierten Termin und &uuml;bertr&auml;gt diesen mit der Funktion <b>AppointmentItemToGoogleCalendar <\/b>in den Google Calendar (siehe <b>Termine von Outlook zum Google Calendar exportieren <\/b>(<b>www.vbentwickler.de\/418<\/b>). Die dabei ermittelte <b>EventID <\/b>f&uuml;r den neuen Google-Termin tr&auml;gt die Funktion anschlie&szlig;end in die Eigenschaft <b>BillingInformationen <\/b>des Outlook-Termins ein. So k&ouml;nnen wir auch Aktualisierungen des Outlook-Termins leicht in den Google Calendar &uuml;bertragen.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>InsertEventInGoogleCalendar()\r\n     <span style=\"color:blue;\">Dim <\/span>objAppointmentItem<span style=\"color:blue;\"> As <\/span>Outlook.AppointmentItem\r\n     <span style=\"color:blue;\">Dim <\/span>strEventID<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> objAppointmentItem = GetSelectedAppointmentItem\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> objAppointmentItem Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n         strEventID = AppointmentItemToGoogleCalendar(objAppointmentItem)\r\n         <span style=\"color:blue;\">With<\/span> objAppointmentItem\r\n             .BillingInformation = strEventID\r\n             .Save\r\n         End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Kein zu exportierender Termin angegeben.\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 8: Diese Prozedur startet den Export eines Termins.<\/span><\/b><\/p>\n<h2>Automatisches &Uuml;bertragen der Termine beim Anlegen, &Auml;ndern und L&ouml;schen<\/h2>\n<p>Gelegentlich kann es n&uuml;tzlich sein, selektiv Termine nach Google zu &uuml;bertragen oder einen dort angelegten Termin von Outlook aus zu l&ouml;schen. Dazu sind die oben beschriebenen Funktionen optimal geeignet.<\/p>\n<p>Aber vielleicht m&ouml;chtest Du alle Termine, die in Outlook angelegt werden, nach Google &uuml;bertragen &#8211; und auch automatisch daf&uuml;r sorgen, dass &Auml;nderungen in Outlook auch nach Google &uuml;bertragen werden. Dann willst Du vermutlich auch, dass Termine, die Du in Outlook l&ouml;schst, auch im Google Calendar entfernt werden. Daf&uuml;r haben wir weitere Funktionen entwickelt, die wir nun beschreiben. Die Grundlage ist das Aktivieren dieser Funktionen in den Optionen von <b>amvAppointmentToGoogle<\/b> (siehe Bild 11).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_011.png\" alt=\"Einstellungen zum automatischen &Uuml;bertragen von Kategorien nach Google\" width=\"649,627\" height=\"395,3386\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 11: Einstellungen zum automatischen &Uuml;bertragen von Kategorien nach Google<\/span><\/b><\/p>\n<p>Hier haben wir vier Optionen:<\/p>\n<ul>\n<li>Termine beim Anlegen automatisch &uuml;bertragen<\/li>\n<li>Mit Google synchronisierte Termine dieser Outlook-Kategorie zuweisen<\/li>\n<li>&Auml;nderungen an Terminen automatisch &uuml;bertragen<\/li>\n<li>Termine beim L&ouml;schen auch in Google Calendar l&ouml;schen<\/li>\n<\/ul>\n<p>Die zweite Option hat folgende Bewandnis: Damit wir sehen, dass das automatische Anlegen funktioniert hat, k&ouml;nnen wir den so behandelten Outlook-Termin mit einer speziellen Kategorie versehen. Dazu k&ouml;nnen wir mit dem Kombinationsfeld eine der aktuell vorhandenen Kategorien selektieren. Legen wir einen Termin mit einer solchen Kategorie an, wird dieser in der &Uuml;bersicht farbig markiert und in der Detailansicht wird die Kategorie &uuml;ber dem <b>Speichern &#038; Schlie&szlig;en<\/b>-Button angezeigt (siehe Bild 12).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_419_012.png\" alt=\"Termin mit Kategorie\" width=\"424,6267\" height=\"291,4597\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 12: Termin mit Kategorie<\/span><\/b><\/p>\n<h2>Automatisches &Uuml;bertragen eines neuen Outlook-Termins in Google<\/h2>\n<p>Weiter oben haben wir die beiden Objektvariablen <b>objCalendar <\/b>und <b>objCalendarItems <\/b>beschrieben. Nun kommen sie zum Einsatz. Die hier verwendeten Ereignisprozeduren erl&auml;utern wir detailliert im Artikel <b>Outlook: Ereignisse f&uuml;r Termine implementieren <\/b>(<b>www.vbentwickler.de\/417<\/b>).<\/p>\n<p>Wenn der Benutzer einen neuen Termin anlegt, l&ouml;st dies das Ereignis <b>ItemAdd <\/b>des Objekts <b>objCalendarItems <\/b>aus (siehe Listing 9).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>objCalendarItems_ItemAdd(ByVal Item<span style=\"color:blue;\"> As Object<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>strEventID<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">If <\/span>GetAppSetting(\"CreateGoogleEventOnAdd\") = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">If <\/span>TestToken(GetAppSetting(\"AccessToken\")) = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n             strEventID = AppointmentItemToGoogleCalendar(Item)\r\n             <span style=\"color:blue;\">With<\/span> Item\r\n                 .BillingInformation = strEventID\r\n                 <span style=\"color:blue;\">If <\/span>GetAppSetting(\"SetCategoryForSynchronizedEvents\") = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n                     .Categories = .Categories & \";\" & GetAppSetting(\"CategoryForSynchronizedEvents\")\r\n                 <span style=\"color:blue;\">End If<\/span>\r\n                 .Save\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> Item = Nothing\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 9: Diese Ereignisprozedur wird beim Hinzuf&uuml;gen eines Termins in Outlook ausgel&ouml;st.<\/span><\/b><\/p>\n<p>Hier wird als Erstes gepr&uuml;ft, ob die Option Termine beim Anlegen automatisch &uuml;bertragen aktiviert ist.<\/p>\n<p>Falls ja, testet die Prozedur, ob das Token funktioniert. Ist auch da erfolgreich, ruft die Prozedur die Funktion <b>AppointmentItemToGoogleCalendar <\/b>auf, die den Termin bei Google eintr&auml;gt und die <b>EventID <\/b>zur&uuml;ckliefert. Diese Funktion beschreiben wir im Artikel <b>Termine von Outlook zum Google Calendar exportieren <\/b>(<b>www.vbentwickler.de\/418<\/b>).<\/p>\n<p>Wenn dann auch noch die Option <b>Mit Google synchronisierte Termine dieser Outlook-Kategorie zuweisen <\/b>aktiviert ist, stellt die Prozedur die Kategorie des neuen Kalenders auf den eingestellten Eintrag ein. Au&szlig;erdem erh&auml;lt die Eigenschaft <b>BillingInformation <\/b>des Outlook-Termins den Inhalt von <b>strEventID<\/b>.<\/p>\n<h2>Automatisches Aktualisieren eines Outlook-Termins in Google<\/h2>\n<p>Beim Aktualisieren eines Termins wird die Prozedur <b>objCalendarItems_ItemChange <\/b>ausgel&ouml;st (siehe Listing 10). Diese pr&uuml;ft, ob die Einstellung <b>&Auml;nderungen an Terminen automatisch &uuml;bertragen <\/b>aktiviert ist.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>objCalendarItems_ItemChange(ByVal Item<span style=\"color:blue;\"> As Object<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>strEventID<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">If <\/span>GetAppSetting(\"CreateGoogleEventOnUpdate\") = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">If <\/span>TestToken(GetAppSetting(\"AccessToken\")) = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n             strEventID = AppointmentItemToGoogleCalendar(Item)\r\n             <span style=\"color:blue;\">With<\/span> Item\r\n                 <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> (strEventID = .Billinginformation)<span style=\"color:blue;\"> Then<\/span>\r\n                     .BillingInformation = strEventID\r\n                     .Save\r\n                 <span style=\"color:blue;\">End If<\/span>\r\n             End <span style=\"color:blue;\">With<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 10: Diese Ereignisprozedur wird beim Aktualisieren eines Termins in Outlook ausgel&ouml;st.<\/span><\/b><\/p>\n<p>In diesem Fall pr&uuml;ft sie auch noch die G&uuml;ltigkeit des Tokens und holt dann mit schreibt dann mit der Funktion <b>AppointmentItemToGoogleCalendar<\/b> die &Auml;nderungen in den entsprechenden Google-Termin.<\/p>\n<p>Das wir die gleiche Funktion wie beim Anlegen nutzen, hat den Hintergrund, dass es sein kann, dass der Termin noch nicht angelegt wurde. Die Funktion <b>AppointmentItemToGoogleCalendar <\/b>erledigt beides.<\/p>\n<p>Falls der Termin noch nicht in Google angelegt war, wird die ermittelte <b>EventID <\/b>auch hier in die <b>BillingInformation <\/b>des Outlook-Termins eingetragen.<\/p>\n<h2>Automatisches L&ouml;schen eines Outlook-Termins in Google<\/h2>\n<p>Das L&ouml;schen erledigen wir durch das Ereignis <b>BeforeItemMove <\/b>von <b>objCalendar <\/b>&#8211; <b>objCalendarItems <\/b>liefert kein passendes Ereignis (siehe Listing 11).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>objCalendar_BeforeItemMove(ByVal Item<span style=\"color:blue;\"> As Object<\/span>, ByVal MoveTo<span style=\"color:blue;\"> As <\/span>Outlook.MAPIFolder, Cancel<span style=\"color:blue;\"> As Boolean<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>strEventID<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strAccessToken<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strCalendarID<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">If <\/span>GetAppSetting(\"DeleteGoogleEventOnDelete\") = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">If <\/span>TestToken(GetAppSetting(\"AccessToken\")) = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n             strEventID = Item.BillingInformation\r\n             <span style=\"color:blue;\">If <\/span>MoveTo = GetDefaultDeletedItemsFolder()<span style=\"color:blue;\"> Then<\/span>\r\n                 <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> <span style=\"color:blue;\">Len<\/span>(strEventID) = 0<span style=\"color:blue;\"> Then<\/span>\r\n                     strAccessToken = GetAppSetting(\"AccessToken\")\r\n                     strCalendarID = GetAppSetting(\"CalendarID\")\r\n                     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> DeleteEvent(strAccessToken, strCalendarID, strEventID)<span style=\"color:blue;\"> Then<\/span>\r\n                         <span style=\"color:blue;\">MsgBox<\/span> \"Termin konnte nicht aus Google gel&ouml;scht werden.\", vbOKOnly + vbExclamation, _\r\n                             \"L&ouml;schen fehlgeschlagen\"\r\n                     <span style=\"color:blue;\">End If<\/span>\r\n                 <span style=\"color:blue;\">End If<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 11: Diese Ereignisprozedur wird vor dem Verschieben eines Termins in Outlook ausgel&ouml;st.<\/span><\/b><\/p>\n<p>Der Vorgang l&auml;uft genauso wie die beiden vorherigen ab, was die &Uuml;berpr&uuml;fungen angeht. Die Prozedur holt dann die <b>EventID <\/b>aus der Eigenschaft <b>BillingID <\/b>des Outlook-Termine und pr&uuml;ft noch, ob der mit dem Parameter <b>MoveTo <\/b>&uuml;bergebene Zielordner f&uuml;r das Verschieben der Ordner mit den gel&ouml;schten Elementen ist.<\/p>\n<p>Ist <b>strEvent <\/b>dann gef&uuml;llt, holt die Prozedur noch das Token und die <b>CalendarID <\/b>und ruft mit diesen Informationen die Funktion <b>DeleteEvent <\/b>auf. Diese haben wir bereits im Artikel <b>Google Calendar per Rest-API programmieren <\/b>(<b>www.vbentwickler.de\/410<\/b>) beschrieben. Wenn das fehlschl&auml;gt, wird eine Meldung ausgegeben.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Dieser Artikel beschreibt die Funktionsweise eines COM-Add-Ins, dass wir in Outlook einsetzen k&ouml;nnen, um Termine von Outlook nach Google zu exportieren und diese auf dem aktuellen Stand in Outlook zu halten.<\/p>\n<p>Als Extra haben wir noch ein Setup vorbereitet, welches die auf den ersten Seiten abgebildeten Schritte verk&uuml;rzt. <\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>amvGoogleOAuth2.zip<\/p>\n<p>amvAppointmentToGoogle.zip<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/A3C7FF8E-DE5F-47FB-82BB-2B17EA8DCD41\/vbe_419.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In verschiedenen anderen Artikeln haben wir die Techniken vorgestellt, mit denen per VBA wir Termine in den Google Calendar eintragen k&ouml;nnen und auf Ereignisse wie das Anlegen, &Auml;ndern oder L&ouml;schen von Terminen in Outlook reagieren k&ouml;nnen. Wir wollen dies nun alles in einem COM-Add-In zusammenbringen. Das COM-Add-In stellt Ribbon- und Kontextmen&uuml;-Eintr&auml;ge bereit, mit denen man Termine per Mausklick nach Google &uuml;bertragen kann und h&auml;lt Automatismen bereit, die daf&uuml;r sorgen, dass &Auml;nderungen an Terminen wie das Anlegen, Bearbeiten oder L&ouml;schen automatisch in den Google Calendar &uuml;bertragen werden. Damit ist nur noch eine kurze Installation n&ouml;tig, um diese Funktionen in Outlook bereitzustellen.<\/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":[66012024,662024,44000027,44000026],"tags":[],"yst_prominent_words":[],"class_list":["post-55000419","post","type-post","status-publish","format-standard","hentry","category-66012024","category-662024","category-Excel_programmieren","category-Outlook_programmieren"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000419","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=55000419"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000419\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000419"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000419"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000419"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000419"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}