{"id":55001612,"date":"2026-08-01T00:00:00","date_gmt":"2026-07-05T11:03:27","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1612"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"VBAModule_regelmaessig_sichern_per_COMAddIn","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/","title":{"rendered":"VBA-Module regelm&auml;&szlig;ig sichern per COM-Add-In"},"content":{"rendered":"<p><b>Wer viel in VBA programmiert, kennt den Schreck: Ein Absturz von Access, und die Arbeit der letzten halben Stunde ist weg. Nat&uuml;rlich kann man von Hand speichern &#8211; aber genau das vergisst man im Eifer des Gefechts. In diesem Beitrag bauen wir uns einen flei&szlig;igen Helfer, der uns diese Sorge abnimmt: ein COM-Add-In f&uuml;r den Access-Entwickler, das in einstellbaren Abst&auml;nden alle ge&auml;nderten Formulare, Berichte und Module automatisch in einzelne Textdateien sichert. So haben wir immer eine frische Kopie unseres Codes zur Hand, ganz ohne daran zu denken.<\/b><\/p>\n<h2>Was der Helfer leisten soll<\/h2>\n<p>Bevor wir in den Code schauen, halten wir kurz fest, was das Add-In eigentlich tun soll. In regelm&auml;&szlig;igen Abst&auml;nden &#8211; zum Beispiel alle f&uuml;nf Minuten &#8211; sieht es nach, welche Module im aktuellen Projekt seit dem letzten Speichern ge&auml;ndert wurden. Jedes solche Modul schreibt es als eigene Textdatei in einen Unterordner namens <b>BAS<\/b>. Damit dabei nicht bei jedem Durchlauf s&auml;mtliche Module erneut gesichert werden, merkt sich das Add-In f&uuml;r jedes Modul einen kleinen Fingerabdruck des Inhalts und legt nur dann eine neue Datei an, wenn sich wirklich etwas ge&auml;ndert hat.<\/p>\n<p>Bedienen l&auml;sst sich der Helfer bequem &uuml;ber einen eigenen Bereich im Datei-Men&uuml; des VBA-Editors. Dort schalten wir die automatische Sicherung ein und aus, w&auml;hlen das Zeitintervall und sehen auf einen Blick, welche Dateien zuletzt gesichert wurden. Wie dieser Bereich aussieht, zeigt Bild 1.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_04\/pic_1612_001.png\" alt=\"Das COM-Add-In amvCodeBackup im Dateinmen&uuml; von Access\" width=\"599,559\" height=\"387,7838\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Das COM-Add-In amvCodeBackup im Dateinmen&uuml; von Access<\/span><\/b><\/p>\n<p>In Bild 2 sehen Sie, wie das Intervall zum Exportieren der ge&auml;nderten VBA-Module ausgew&auml;hlt wird.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_04\/pic_1612_002.png\" alt=\"Ausw&auml;hlen des Intervalls zum Anlegen von Sicherungen\" width=\"599,559\" height=\"388,4745\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Ausw&auml;hlen des Intervalls zum Anlegen von Sicherungen<\/span><\/b><\/p>\n<h2>Ein kleiner Wermutstropfen<\/h2>\n<p>Das Sichern bezieht sich nicht nur auf reine VBA-Module &#8211; auch wenn der Code in den Klassenmodulen von Formularen oder Berichten ge&auml;ndert wurden, werden diese gespeichert.<\/p>\n<p>Wenn allerdings der Code eines Formulars oder Berichts ge&auml;ndert wurde, muss dieses vor dem Exportieren der neuen Version zun&auml;chst gespeichert werden &#8211; daher erscheint in diesem Fall jeweils eine Meldung wie die aus Bild 3.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_04\/04-07-2026_20-03-03.PNG\" alt=\"Meldung vor dem Speichern von Formularen oder Berichten\" width=\"699,559\" height=\"198,3002\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Meldung vor dem Speichern von Formularen oder Berichten<\/span><\/b><\/p>\n<p>Damit dies beim Programmieren nicht zu sehr st&ouml;rt, empfiehlt es sich, das Intervall entsprechend hochzusetzen. Welches Intervall f&uuml;r Sie optimal funktioniert, probieren Sie am besten selbst aus. Ein kleineres Intervall bringt diese Meldung &ouml;fter hervor, ein gr&ouml;&szlig;eres ist mit dem Risiko behaftet, dass bei einem Absturz von Access eventuell mehr &Auml;nderungen verloren gehen.<\/p>\n<h2>Das Projekt in twinBASIC<\/h2>\n<p>Wie in fr&uuml;heren Beitr&auml;gen entsteht das COM-Add-In mit twinBASIC, das f&uuml;r die 32-Bit-Version kostenlos ist. Wie man ein solches Projekt von Grund auf anlegt, haben wir bereits an anderer Stelle gezeigt; hier konzentrieren wir uns darauf, wie das fertige Projekt <b>amvCodeBackup<\/b> und der darin enthaltene Code funktionieren. Am bequemsten &ouml;ffnen Sie die mitgelieferte Projektdatei und verfolgen die Bausteine parallel zum Beitrag.<\/p>\n<p>Das Projekt gliedert sich in drei Teile. Die Klasse <b>amvCodeBackup<\/b> steuert den gesamten Ablauf. Das Modul <b>mdlTimer<\/b> verbindet den Windows-Wecker mit der Sicherungsroutine, und das Modul <b>DllRegistration<\/b> macht das Add-In gegen&uuml;ber Access bekannt. Diese drei Teile gehen wir nun der Reihe nach durch.<\/p>\n<h2>Der Kopf der Klasse<\/h2>\n<p>Ganz oben in der Klasse <b>amvCodeBackup <\/b>steht die <b>ClassId<\/b>, eine weltweit einmalige Nummer, unter der Windows das Add-In in der Registry wiederfindet. Sie wird von twinBASIC einmalig vergeben und danach nicht mehr angetastet, denn eine ge&auml;nderte Nummer g&auml;lte als ein g&auml;nzlich anderes Objekt. Direkt darunter meldet die Klasse die beiden Bausteine an, die sie umsetzt. Hier sehen wir den Kopf der Klasse:<\/p>\n<pre>[ClassId(\"DC8E7154-FAC8-43B7-88E5-A9352753999F\")]\r\n<span style=\"color:blue;\">Class<\/span> amvCodeBackup\r\n    Implements IDTExtensibility2\r\n    [WithDispatchForwarding]\r\n    Implements IRibbonExtensibility<\/pre>\n<p>Eine Schnittstelle legt fest, welche Prozeduren eine Klasse bereitstellen muss. &Uuml;ber <b>IDTExtensibility2<\/b> kann Office das Add-In starten und wieder beenden, und <b>IRibbonExtensibility<\/b> &ouml;ffnet uns den Weg, dem Men&uuml;band eigene Elemente hinzuzuf&uuml;gen. Die Angabe <b>WithDispatchForwarding<\/b> in eckigen Klammern ist eine technische Markierung, die twinBASIC f&uuml;r das Ribbon ben&ouml;tigt.<\/p>\n<h2>Was sich die Klasse merkt<\/h2>\n<p>Damit der Helfer arbeiten kann, h&auml;lt er mehrere Verweise fest. Hier sehen wir diese im &Uuml;berblick:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>objAccess<span style=\"color:blue;\"> As <\/span>Access.Application\r\n<span style=\"color:blue;\">Private <\/span>objVBE<span style=\"color:blue;\"> As <\/span>VBIDE.VBE\r\n<span style=\"color:blue;\">Private <\/span>cbbCodeBackup<span style=\"color:blue;\"> As Object<\/span>\r\n<span style=\"color:blue;\">Private <\/span>objRibbon<span style=\"color:blue;\"> As <\/span>IRibbonUI\r\n<span style=\"color:blue;\">Private <\/span>WithEvents cbbCodeBackupEvents<span style=\"color:blue;\"> As <\/span>VBIDE.CommandBarEvents\r\n<span style=\"color:blue;\">Private <\/span>cbbCodeBackupOptionen<span style=\"color:blue;\"> As Object<\/span>\r\n<span style=\"color:blue;\">Private <\/span>WithEvents cbbCodeBackupOptionenEvents<span style=\"color:blue;\"> As <\/span>VBIDE.CommandBarEvents<\/pre>\n<p>In <b>objAccess<\/b> liegt sp&auml;ter Access selbst, in <b>objVBE<\/b> der VBA-Editor mit seinen Projekten und Modulen &#8211; an ihn wenden wir uns, um an den Quellcode zu gelangen. In <b>objRibbon<\/b> merken wir uns das Men&uuml;band, um seine Anzeige bei Bedarf auffrischen zu k&ouml;nnen. Die vier &uuml;brigen Variablen geh&ouml;ren zu zwei Eintr&auml;gen, die wir zus&auml;tzlich in die Symbolleiste des Editors setzen: <b>cbbCodeBackup<\/b> zum Ein- und Ausschalten und <b>cbbCodeBackupOptionen<\/b> zum Einstellen des Intervalls. Das Schl&uuml;sselwort <b>WithEvents<\/b> sorgt jeweils daf&uuml;r, dass wir auf Klicks reagieren k&ouml;nnen.<\/p>\n<h2>Ein Wecker aus Windows<\/h2>\n<p>Um in festen Abst&auml;nden aktiv zu werden, borgt sich der Helfer eine Uhr von Windows. Die Funktion <b>SetTimer<\/b> stellt einen Wecker, der nach der eingestellten Zeit ausl&ouml;st, <b>KillTimer<\/b> schaltet ihn wieder ab. Weil Access in einer 32- und einer 64-Bit-Version existiert und diese Funktionen dort geringf&uuml;gig anders angesprochen werden, sind sie doppelt vereinbart. Welche Fassung gilt, entscheidet bereits beim Kompilieren die Abfrage <b>#If Win64 Then<\/b> (siehe Listing 1).<\/p>\n<pre>#If Win64 Then\r\n    <span style=\"color:blue;\">Private <\/span>Declare PtrSafe Function SetTimer Lib \"user32\" (ByVal hWnd<span style=\"color:blue;\"> As Long<\/span>Ptr, ByVal nIDEvent<span style=\"color:blue;\"> As Long<\/span>Ptr, _\r\n        ByVal uElapse<span style=\"color:blue;\"> As Long<\/span>, ByVal lpTimerFunc<span style=\"color:blue;\"> As Long<\/span>Ptr)<span style=\"color:blue;\"> As Long<\/span>Ptr\r\n    <span style=\"color:blue;\">Private <\/span>Declare PtrSafe Function KillTimer Lib \"user32\" (ByVal hWnd<span style=\"color:blue;\"> As Long<\/span>Ptr, ByVal nIDEvent<span style=\"color:blue;\"> As Long<\/span>Ptr)<span style=\"color:blue;\"> As Long<\/span>\r\n    <span style=\"color:blue;\">Private <\/span>lngTimerID<span style=\"color:blue;\"> As Long<\/span>Ptr\r\n#Else\r\n    <span style=\"color:blue;\">Private <\/span>Declare Function SetTimer Lib \"user32\" (ByVal hWnd<span style=\"color:blue;\"> As Long<\/span>, ByVal nIDEvent<span style=\"color:blue;\"> As Long<\/span>, _\r\n        ByVal uElapse<span style=\"color:blue;\"> As Long<\/span>, ByVal lpTimerFunc<span style=\"color:blue;\"> As Long<\/span>)<span style=\"color:blue;\"> As Long<\/span>\r\n    <span style=\"color:blue;\">Private <\/span>Declare Function KillTimer Lib \"user32\" (ByVal hWnd<span style=\"color:blue;\"> As Long<\/span>, ByVal nIDEvent<span style=\"color:blue;\"> As Long<\/span>)<span style=\"color:blue;\"> As Long<\/span>\r\n    <span style=\"color:blue;\">Private <\/span>lngTimerID<span style=\"color:blue;\"> As Long<\/span>\r\n#End If<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Die Timer-Funktionen werden je nach Architektur passend deklariert<\/span><\/b><\/p>\n<p><b>SetTimer<\/b> liefert eine Kennnummer f&uuml;r den laufenden Wecker zur&uuml;ck, die wir in <b>lngTimerID<\/b> ablegen. Solange sie ungleich null ist, wissen wir, dass der Timer arbeitet &#8211; ein bequemer Weg, den Ein-\/Aus-Zustand abzufragen.<\/p>\n<h2>Das Intervall dauerhaft merken<\/h2>\n<p>Das gew&auml;hlte Intervall soll einen Neustart von Access &uuml;berdauern, deshalb legen wir es in der Registry ab. Unter welchem Namen und in welchem Abschnitt der Wert liegt, halten ein paar Konstanten fest; fehlt noch ein gespeicherter Wert, greifen 300 Sekunden als Vorgabe. Die Konstanten dazu sehen wir folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>Const REG_APP<span style=\"color:blue;\"> As String<\/span> = \"amvCodeBackup\"\r\n<span style=\"color:blue;\">Private <\/span>Const REG_SECTION<span style=\"color:blue;\"> As String<\/span> = \"Optionen\"\r\n<span style=\"color:blue;\">Private <\/span>Const REG_INTERVALL<span style=\"color:blue;\"> As String<\/span> = \"Intervall\"\r\n<span style=\"color:blue;\">Private <\/span>Const REG_INTERVALL_DEFAULT<span style=\"color:blue;\"> As Long<\/span> = 300\r\n<span style=\"color:blue;\">Private <\/span>Const MAX_RECENT<span style=\"color:blue;\"> As Integer<\/span> = 10<\/pre>\n<p>Das eigentliche Lesen und Schreiben &uuml;bernehmen zwei winzige Prozeduren, die sich der fertigen VBA-Funktionen <b>GetSetting<\/b> und <b>SaveSetting<\/b> bedienen. Mehr als der gespeicherte Sekundenwert wandert hier nicht &uuml;ber die Leitung, wie folgende Listings belegen:<\/p>\n<pre><span style=\"color:blue;\">Private Function <\/span>IntervallLesen()<span style=\"color:blue;\"> As Long<\/span>\r\n    IntervallLesen = CLng(GetSetting(REG_APP, REG_SECTION, _\r\n        REG_INTERVALL, REG_INTERVALL_DEFAULT))\r\n<span style=\"color:blue;\">End Function<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>IntervallSchreiben(lngSekunden<span style=\"color:blue;\"> As Long<\/span>)\r\n    SaveSetting REG_APP, REG_SECTION, REG_INTERVALL, _\r\n        lngSekunden\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die Konstante <b>MAX_RECENT<\/b> aus dem vorigen Listing legt &uuml;brigens fest, wie viele der zuletzt gesicherten Dateien wir sp&auml;ter in der Liste anzeigen &#8211; hier also zehn.<\/p>\n<h2>Die Oberfl&auml;che als XML beschreiben<\/h2>\n<p>Der Bereich im Datei-Men&uuml; wird nicht zusammengeklickt, sondern als XML beschrieben. Die Funktion <b>GetCustomUI<\/b> setzt diesen Text Zeile f&uuml;r Zeile zusammen und reicht ihn an Office weiter. Der Vorteil dieser Trennung: Das XML bestimmt allein das Aussehen und die Anordnung, w&auml;hrend der eigentliche Inhalt sp&auml;ter aus dem Code kommt. Die Definition zeigt Listing 2.<\/p>\n<pre><span style=\"color:blue;\">Private Function <\/span>GetCustomUI(ByVal RibbonID<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span> Implements IRibbonExtensibility.GetCustomUI\r\n    <span style=\"color:blue;\">Dim <\/span>strXML<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Integer<\/span>\r\n    strXML = \"&lt;customUI xmlns=\"\"http:\/\/schemas.microsoft.com\/office\/2009\/07\/customui\"\" onLoad=\"\"customUI_OnLoad\"\"&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"  &lt;backstage&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"    &lt;tab id=\"\"tabCodeBackup\"\" label=\"\"amvCodeBackup\"\" insertAfterMso=\"\"TabInfo\"\" \" _\r\n        & \"title=\"\"amvCodeBackup\"\"&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"      &lt;firstColumn&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"        &lt;group id=\"\"grpTimer\"\" label=\"\"Timer\"\"&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"          &lt;topItems&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"            &lt;button id=\"\"btnTimer\"\" getLabel=\"\"btnTimer_GetLabel\"\" \" _\r\n        & \"onAction=\"\"btnTimer_OnAction\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"            &lt;dropDown id=\"\"ddIntervall\"\" label=\"\"Intervall\"\" \" _\r\n        & \"getSelectedItemIndex=\"\"ddIntervall_GetSelectedItemIndex\"\" onAction=\"\"ddIntervall_OnAction\"\"&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"              &lt;item id=\"\"i10\"\" label=\"\"10 Sekunden\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"              &lt;item id=\"\"i20\"\" label=\"\"20 Sekunden\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"              &lt;item id=\"\"i30\"\" label=\"\"30 Sekunden\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"              &lt;item id=\"\"i60\"\" label=\"\"1 Minute\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"              &lt;item id=\"\"i120\"\" label=\"\"2 Minuten\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"              &lt;item id=\"\"i300\"\" label=\"\"5 Minuten\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"              &lt;item id=\"\"i600\"\" label=\"\"10 Minuten\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"            &lt;\/dropDown&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"          &lt;\/topItems&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"        &lt;\/group&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"        &lt;group id=\"\"grpRecent\"\" label=\"\"Zuletzt gesichert\"\"&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"          &lt;topItems&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    For i = 1 To MAX_RECENT\r\n        strXML = strXML & \"            &lt;labelControl id=\"\"lblRecent\" & i & \"\"\" getLabel=\"\"lblRecent\" & i _\r\n            & \"_GetLabel\"\"\/&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    <span style=\"color:blue;\">Next<\/span> i\r\n    strXML = strXML & \"          &lt;\/topItems&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"        &lt;\/group&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"      &lt;\/firstColumn&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"    &lt;\/tab&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"  &lt;\/backstage&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    strXML = strXML & \"&lt;\/customUI&gt;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n    GetCustomUI = strXML\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Die Funktion GetCustomUI liefert die Ribbon-Definition.<\/span><\/b><\/p>\n<p>Das &auml;u&szlig;erste Element <b>customUI<\/b> klammert alles ein und nennt &uuml;ber <b>onLoad<\/b> die Prozedur, die Office einmal aufruft, sobald die Oberfl&auml;che geladen ist. Darin folgt <b>backstage<\/b> &#8211; jener Bereich, der beim Klick auf Datei erscheint. Mit <b>tab<\/b> legen wir dort eine eigene Seite an und schieben sie &uuml;ber <b>insertAfterMso<\/b> hinter den vorhandenen Bereich. Die erste Gruppe <b>grpTimer<\/b> enth&auml;lt die Schaltfl&auml;che <b>btnTimer<\/b> zum Umschalten und das Auswahlfeld <b>ddIntervall<\/b> mit den festen Zeitstufen. Jedes Element nennt dabei die Funktionen, die es mit Leben f&uuml;llen &#8211; etwa <b>getLabel<\/b> f&uuml;r die Beschriftung und <b>onAction<\/b> f&uuml;r die Reaktion auf einen Klick.<\/p>\n<p>Die zweite Gruppe listet die zuletzt gesicherten Dateien auf. Weil es davon zehn sind, w&auml;re es m&uuml;hsam, jede Zeile von Hand zu schreiben &#8211; eine Schleife erzeugt sie stattdessen.<\/p>\n<p>Jede Zeile erh&auml;lt eine eigene Kennung von <b>lblRecent1<\/b> bis <b>lblRecent10<\/b> und nennt &uuml;ber <b>getLabel<\/b> die Funktion, die ihren Text liefert. Danach schlie&szlig;t die Definition Gruppe, Spalte, Seite und Bereich wieder ordentlich, und die fertige Zeichenkette geht zur&uuml;ck an Office.<\/p>\n<h2>Das Men&uuml;band greifbar halten<\/h2>\n<p>Sobald Office unsere Oberfl&auml;che geladen hat, ruft es die in <b>onLoad<\/b> genannte Funktion auf. Sie tut nur eines, ist aber wichtig: Sie merkt sich das &uuml;bergebene Men&uuml;band in <b>objRibbon<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>customUI_OnLoad(ByVal ribbon<span style=\"color:blue;\"> As <\/span>IRibbonUI)\r\n    <span style=\"color:blue;\">Set<\/span> objRibbon = ribbon\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>&Uuml;ber diesen gemerkten Verweis k&ouml;nnen wir sp&auml;ter einzelne Steuerelemente auffrischen lassen &#8211; etwa die Beschriftung der Umschalt-Schaltfl&auml;che, sobald sich der Zustand &auml;ndert, oder die ganze Liste der letzten Sicherungen, wenn eine neue Datei hinzugekommen ist.<\/p>\n<h2>Woher die Liste der letzten Sicherungen kommt<\/h2>\n<p>Jede der zehn Listenzeilen fragt beim Anzeigen ihre Beschriftung ab. Damit daf&uuml;r nicht zehn fast gleiche Funktionen n&ouml;tig sind, reichen sie ihre Nummer an eine gemeinsame Helferin namens <b>GetRecentLabel<\/b> weiter. Diese liest alle Sicherungsdateien im BAS-Ordner ein und ordnet sie nach Datum, sodass die neueste obenauf liegt. Den Kern dieser Sortierung h&auml;lt Listing 3 fest.<\/p>\n<pre><span style=\"color:blue;\">Private Function <\/span>GetRecentLabel(intPos<span style=\"color:blue;\"> As Integer<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strFolder<span style=\"color:blue;\"> As String<\/span>, strFile<span style=\"color:blue;\"> As String<\/span>, colFiles()<span style=\"color:blue;\"> As String<\/span>, intCount<span style=\"color:blue;\"> As Integer<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Integer<\/span>, j<span style=\"color:blue;\"> As Integer<\/span>, strTemp<span style=\"color:blue;\"> As String<\/span>, dtTemp<span style=\"color:blue;\"> As Date<\/span>, dtFile<span style=\"color:blue;\"> As Date<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strFullName<span style=\"color:blue;\"> As String<\/span>, dtMod<span style=\"color:blue;\"> As Date<\/span>, objVBProject<span style=\"color:blue;\"> As <\/span>VBIDE.VBProject\r\n    <span style=\"color:blue;\">Set<\/span> objVBProject = GetVBProject()\r\n    <span style=\"color:blue;\">If <\/span>objVBProject Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n        GetRecentLabel = \"\"\r\n        <span style=\"color:blue;\">Exit Function<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    strFolder = <span style=\"color:blue;\">Left<\/span>(objVBProject.FileName, <span style=\"color:blue;\">InStrRev<\/span>(objVBProject.FileName, \"\\\")) & \"BAS\"\r\n    <span style=\"color:blue;\">If <\/span>Dir(strFolder, vbDirectory) = \"\"<span style=\"color:blue;\"> Then<\/span>\r\n        GetRecentLabel = \"\"\r\n        <span style=\"color:blue;\">Exit Function<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    ReDim colFiles(0)\r\n    intCount = 0\r\n    strFile = Dir(strFolder & \"\\*.bas\")\r\n    <span style=\"color:blue;\">Do While<\/span> strFile &lt;&gt; \"\"\r\n        ReDim Preserve colFiles(intCount)\r\n        colFiles(intCount) = strFile\r\n        intCount = intCount + 1\r\n        strFile = Dir()\r\n    <span style=\"color:blue;\">Loop<\/span>\r\n    <span style=\"color:blue;\">If <\/span>intCount = 0<span style=\"color:blue;\"> Then<\/span>\r\n        GetRecentLabel = \"\"\r\n        <span style=\"color:blue;\">Exit Function<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    For i = 0 To intCount - 2\r\n        For j = 0 To intCount - 2 - i\r\n            <span style=\"color:blue;\">If <\/span>FileDateTime(strFolder & \"\\\" & colFiles(j)) &lt; FileDateTime(strFolder & \"\\\" & colFiles(j + 1))<span style=\"color:blue;\"> Then<\/span>\r\n                strTemp = colFiles(j)\r\n                colFiles(j) = colFiles(j + 1)\r\n                colFiles(j + 1) = strTemp\r\n            <span style=\"color:blue;\">End If<\/span>\r\n        <span style=\"color:blue;\">Next<\/span> j\r\n    <span style=\"color:blue;\">Next<\/span> i\r\n    <span style=\"color:blue;\">If <\/span>intPos &gt; intCount<span style=\"color:blue;\"> Then<\/span>\r\n        GetRecentLabel = \"\"\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        strFullName = colFiles(intPos - 1)\r\n        dt<span style=\"color:blue;\">Mod<\/span> = FileDateTime(strFolder & \"\\\" & strFullName)\r\n        GetRecentLabel = Format(dtMod, \"DD.MM.YY HH:NN:SS\") & \"  \" & strFullName\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 3: Die Sicherungsdateien werden nach Datum geordnet, die j&uuml;ngste zuerst<\/span><\/b><\/p>\n<p>Zum Einsatz kommt hier das Bubble-Sort-Verfahren: Es vergleicht immer zwei benachbarte Dateien und vertauscht sie, falls die &auml;ltere vor der j&uuml;ngeren steht. Nach einigen Durchg&auml;ngen wandert so die neueste Datei nach oben. Zur gew&uuml;nschten Position liefert die Funktion anschlie&szlig;end Datum, Uhrzeit und Dateinamen zur&uuml;ck &#8211; oder einen leeren Text, falls es noch nicht so viele Sicherungen gibt.<\/p>\n<h2>Das Auswahlfeld f&uuml;r das Intervall<\/h2>\n<p>Damit das Auswahlfeld beim &Ouml;ffnen die richtige Stufe hervorhebt, f&uuml;hrt der Code die sieben m&ouml;glichen Werte in einem kleinen Feld. Beim Anzeigen sucht er darin den gespeicherten Wert und meldet dessen Position zur&uuml;ck, sodass genau diese Stufe vorausgew&auml;hlt erscheint:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>intervallWerte(6)<span style=\"color:blue;\"> As Long<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>IntervallWerteInitialisieren()\r\n    intervallWerte(0) = 10 : intervallWerte(1) = 20\r\n    intervallWerte(2) = 30 : intervallWerte(3) = 60\r\n    intervallWerte(4) = 120 : intervallWerte(5) = 300\r\n    intervallWerte(6) = 600\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Function <\/span>ddIntervall_GetSelectedItemIndex( _\r\n        ByVal control<span style=\"color:blue;\"> As <\/span>IRibbonControl)<span style=\"color:blue;\"> As Integer<\/span>\r\n    IntervallWerteInitialisieren()\r\n    <span style=\"color:blue;\">Dim <\/span>intAktuell<span style=\"color:blue;\"> As Integer<\/span>, i<span style=\"color:blue;\"> As Integer<\/span>\r\n    intAktuell = IntervallLesen()\r\n    For i = 0 To <span style=\"color:blue;\">UBound<\/span>(intervallWerte)\r\n        <span style=\"color:blue;\">If <\/span>intervallWerte(i) = intAktuell<span style=\"color:blue;\"> Then<\/span>\r\n            ddIntervall_GetSelectedItemIndex = i\r\n            <span style=\"color:blue;\">Exit Function<\/span>\r\n        <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Next<\/span> i\r\n    ddIntervall_GetSelectedItemIndex = 5\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>W&auml;hlt der Benutzer eine andere Stufe, tritt <b>ddIntervall_OnAction<\/b> auf den Plan: Die Prozedur schreibt den neuen Wert in die Registry und startet einen laufenden Timer mit dem ge&auml;nderten Intervall neu, damit die Umstellung sofort greift.<\/p>\n<h2>Ein- und ausschalten &uuml;ber die Schaltfl&auml;che<\/h2>\n<p>Die Umschalt-Schaltfl&auml;che kennt zwei Aufgaben. Zum einen liefert sie &uuml;ber <b>btnTimer_GetLabel<\/b> ihre eigene Beschriftung, die je nach Zustand &#8222;aktivieren&#8220; oder &#8222;deaktivieren&#8220; lautet. Zum anderen schaltet <b>btnTimer_OnAction<\/b> beim Klick zwischen an und aus um und frischt danach die Anzeige auf. Beide Routinen sehen wir hier:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>btnTimer_GetLabel(control<span style=\"color:blue;\"> As <\/span>_\r\n        IRibbonControl)<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">If <\/span>TimerLaeuft()<span style=\"color:blue;\"> Then<\/span>\r\n        btnTimer_GetLabel = \"Code-Backup deaktivieren\"\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        btnTimer_GetLabel = \"Code-Backup aktivieren\"\r\n    <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>btnTimer_OnAction(control<span style=\"color:blue;\"> As <\/span>IRibbonControl)\r\n    <span style=\"color:blue;\">If <\/span>TimerLaeuft()<span style=\"color:blue;\"> Then<\/span>\r\n        TimerStoppen()\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        TimerStarten()\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    objRibbon.Invalidate\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Der Aufruf <b>objRibbon.Invalidate<\/b> am Ende ist der Grund, warum wir uns das Men&uuml;band zuvor gemerkt haben: Er weist Office an, die Anzeige neu aufzubauen, sodass die Schaltfl&auml;che sofort ihre aktualisierte Beschriftung zeigt.<\/p>\n<h2>Wenn Access das Add-In l&auml;dt<\/h2>\n<p>Sobald Access startet und unser Add-In verbindet, ruft es <b>OnConnection<\/b> auf. Hier richten wir alles ein, was der Helfer sp&auml;ter braucht (siehe Listing 4): Wir merken uns Access und den VBA-Editor, hinterlegen einen Verweis auf unsere eigene Instanz im Timer-Modul und legen &uuml;ber <b>CreateToolBar<\/b> die beiden Men&uuml;eintr&auml;ge an.<\/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>, ByRef custom<span style=\"color:blue;\"> As Variant<\/span>()) _\r\n        Implements IDTExtensibility2.OnConnection\r\n    <span style=\"color:blue;\">On Error GoTo<\/span> fehler\r\n    IntervallWerteInitialisieren()\r\n    <span style=\"color:blue;\">Set<\/span> objAccess = Application\r\n    <span style=\"color:blue;\">Set<\/span> objVBE = objAccess.VBE\r\n    <span style=\"color:blue;\">Set<\/span> mdlTimer.addinInstanz = Me\r\n    CreateToolBar()\r\n    <span style=\"color:blue;\">Exit Sub<\/span>\r\nFehler:\r\n    <span style=\"color:blue;\">MsgBox<\/span>(Err.Number & \" \" & Err.Description & \" \" & Erl, _\r\n        vbOKOnly + <span style=\"color:blue;\">vbCr<\/span>itical, \"OnConnection\")\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Beim Verbinden richtet sich das Add-In ein<\/span><\/b><\/p>\n<p>Der Zeile <b>Set mdlTimer.addinInstanz = Me<\/b> geb&uuml;hrt besondere Aufmerksamkeit &#8211; ihre Bedeutung wird beim Timer gleich klar. Die Schnittstelle <b>IDTExtensibility2<\/b> verlangt noch vier weitere Prozeduren: <b>OnDisconnection<\/b>, <b>OnAddInsUpdate<\/b>, <b>OnStartupComplete<\/b> und <b>OnBeginShutdown<\/b>. Die meisten davon bleiben schlicht, doch <b>OnStartupComplete<\/b> hat eine Aufgabe: Es startet den Timer, sobald Access vollst&auml;ndig geladen ist. Beim Beenden r&auml;umt <b>OnDisconnection<\/b> wieder auf.<\/p>\n<h2>Zwei Eintr&auml;ge in der Symbolleiste<\/h2>\n<p>Neben dem Bereich im Datei-Men&uuml; setzt das Add-In zwei Eintr&auml;ge in das Add-Ins-Men&uuml; des VBA-Editors. Das erledigt <b>CreateToolBar<\/b>, indem es zun&auml;chst das vorhandene Men&uuml; &uuml;ber seine feste Kennung aufsp&uuml;rt und dann zwei Schaltfl&auml;chen hinzuf&uuml;gt. Den Ablauf zeigt Listing 5.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>CreateToolBar()\r\n    Const vbeAddinsMenuID<span style=\"color:blue;\"> As Long<\/span> = 30038\r\n    <span style=\"color:blue;\">Dim <\/span>vbeAddinsMenu<span style=\"color:blue;\"> As <\/span>CommandBarControl = objVBE.CommandBars.FindControl(msoControlPopup, vbeAddinsMenuID)\r\n    <span style=\"color:blue;\">Set<\/span> cbbCodeBackup = vbeAddinsMenu.Controls.Add(Temporary:=<span style=\"color:blue;\">True<\/span>)\r\n    <span style=\"color:blue;\">With<\/span> cbbCodeBackup\r\n        .Caption = \"amvCodeBackup\"\r\n        <span style=\"color:blue;\">Set<\/span> cbbCodeBackupEvents = objVBE.Events.CommandBarEvents(cbbCodeBackup)\r\n    End <span style=\"color:blue;\">With<\/span>\r\n    <span style=\"color:blue;\">Set<\/span> cbbCodeBackupOptionen = vbeAddinsMenu.Controls.Add(Temporary:=<span style=\"color:blue;\">True<\/span>)\r\n    <span style=\"color:blue;\">With<\/span> cbbCodeBackupOptionen\r\n        .Caption = \"amvCodeBackup - Intervall setzen\"\r\n        <span style=\"color:blue;\">Set<\/span> cbbCodeBackupOptionenEvents = objVBE.Events.CommandBarEvents(cbbCodeBackupOptionen)\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 5: CreateToolBar h&auml;ngt zwei Eintr&auml;ge in das Add-Ins-Men&uuml;<\/span><\/b><\/p>\n<p>Der erste Eintrag dient zum Ein- und Ausschalten, der zweite zum Setzen des Intervalls. Wichtig ist jeweils die letzte Zeile: Erst indem wir das <b>CommandBarEvents<\/b>-Objekt in unseren mit <b>WithEvents<\/b> vereinbarten Variablen ablegen, kann die Klasse sp&auml;ter &uuml;berhaupt auf Klicks reagieren.<\/p>\n<p>Die Reaktionen auf diese Klicks stehen in zwei Ereignisprozeduren. Die erste schaltet den Timer um, die zweite fragt &uuml;ber eine Eingabebox nach einem neuen Intervall und weist Werte unter zehn Sekunden zur&uuml;ck (siehe Listing 6).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cbbCodeBackupEvents_Click(ByVal CommandBarControl<span style=\"color:blue;\"> As Object<\/span>, handled<span style=\"color:blue;\"> As Boolean<\/span>, CancelDefault<span style=\"color:blue;\"> As Boolean<\/span>)\r\n    <span style=\"color:blue;\">If <\/span>TimerLaeuft()<span style=\"color:blue;\"> Then<\/span>\r\n        TimerStoppen()\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        TimerStarten()\r\n    <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>cbbCodeBackupOptionenEvents_Click(ByVal CommandBarControl<span style=\"color:blue;\"> As Object<\/span>, _\r\n        handled<span style=\"color:blue;\"> As Boolean<\/span>, CancelDefault<span style=\"color:blue;\"> As Boolean<\/span>)\r\n    <span style=\"color:blue;\">Dim <\/span>strEingabe<span style=\"color:blue;\"> As String<\/span>, lngNeu<span style=\"color:blue;\"> As Long<\/span>\r\n    strEingabe = InputBox(\"Intervall in Sekunden (aktuell: \" & IntervallLesen() & \"s):\", _\r\n        \"amvCodeBackup - Intervall\", IntervallLesen())\r\n    <span style=\"color:blue;\">If <\/span>strEingabe = \"\"<span style=\"color:blue;\"> Then<\/span> <span style=\"color:blue;\">Exit Sub<\/span>\r\n    <span style=\"color:blue;\">If <\/span>IsNumeric(strEingabe)<span style=\"color:blue;\"> Then<\/span>\r\n        lngNeu = CLng(strEingabe)\r\n        <span style=\"color:blue;\">If <\/span>lngNeu &lt; 10<span style=\"color:blue;\"> Then<\/span>\r\n            <span style=\"color:blue;\">MsgBox<\/span> \"Mindestintervall: 10 Sekunden.\"\r\n            <span style=\"color:blue;\">Exit Sub<\/span>\r\n        <span style=\"color:blue;\">End If<\/span>\r\n        IntervallSchreiben lngNeu\r\n        <span style=\"color:blue;\">If <\/span>TimerLaeuft()<span style=\"color:blue;\"> Then<\/span>\r\n            TimerStoppen()\r\n            TimerStarten()\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 6: Die beiden Men&uuml;eintr&auml;ge reagieren auf Klicks<\/span><\/b><\/p>\n<p>So l&auml;sst sich das Intervall auf zwei Wegen einstellen &#8211; bequem &uuml;ber die festen Stufen im Datei-Men&uuml; oder frei per Eingabebox &uuml;ber das Add-Ins-Men&uuml;.<\/p>\n<h2>Den Wecker stellen und abschalten<\/h2>\n<p>Das Stellen des Weckers besorgt eine eigene Prozedur. Sie ruft <b>SetTimer<\/b> mit dem gespeicherten Intervall auf &#8211; umgerechnet in Millisekunden, daher die Multiplikation mit 1000 &#8211; und merkt sich die zur&uuml;ckgelieferte Kennnummer.<\/p>\n<p>Das folgende Listing zeigt den Ablauf:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>TimerStarten()\r\n    <span style=\"color:blue;\">On Error GoTo<\/span> Fehler\r\n    lngTimerID = SetTimer(0, 0, IntervallLesen() * 1000, _\r\n        AddressOf mdlTimer.TimerCallback)\r\n    <span style=\"color:blue;\">If <\/span>lngTimerID = 0<span style=\"color:blue;\"> Then<\/span>\r\n        <span style=\"color:blue;\">MsgBox<\/span> \"Timer konnte nicht gestartet werden!\"\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        StatusSetzen <span style=\"color:blue;\">True<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Exit Sub<\/span>\r\nFehler:\r\n    <span style=\"color:blue;\">MsgBox<\/span>(Err.Number & \" \" & Err.Description & \" \" & Erl, _\r\n        vbOKOnly + <span style=\"color:blue;\">vbCr<\/span>itical, \"TimerStarten\")\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Der vierte Wert im Aufruf, <b>AddressOf mdlTimer.TimerCallback<\/b>, ist der springende Punkt: Er nennt Windows die Prozedur, die beim Ausl&ouml;sen des Weckers aufgerufen werden soll. Das Gegenst&uuml;ck <b>TimerStoppen<\/b> ruft <b>KillTimer<\/b> auf, setzt die Kennnummer zur&uuml;ck und schaltet die Anzeige auf inaktiv; die kurze Funktion <b>TimerLaeuft<\/b> pr&uuml;ft schlicht, ob die Kennnummer ungleich null ist.<\/p>\n<h2>Warum es das Modul mdlTimer braucht<\/h2>\n<p>Nun zu der angek&uuml;ndigten Feinheit. Windows kann beim Ausl&ouml;sen des Weckers nur eine gew&ouml;hnliche Prozedur aufrufen, keine Methode einer Klasse. Deshalb liegt der Empf&auml;nger des Weckrufs im Modul <b>mdlTimer<\/b> statt in der Klasse. Listing 7 macht das deutlich.<\/p>\n<pre><span style=\"color:blue;\">Public <\/span>addinInstanz<span style=\"color:blue;\"> As Object<\/span>\r\n#If Win64 Then\r\n<span style=\"color:blue;\">Public Sub <\/span>TimerCallback(ByVal hWnd<span style=\"color:blue;\"> As Long<\/span>Ptr, ByVal uMsg<span style=\"color:blue;\"> As Long<\/span>, ByVal nIDEvent<span style=\"color:blue;\"> As Long<\/span>Ptr, ByVal dwTime<span style=\"color:blue;\"> As Long<\/span>)\r\n#Else\r\n<span style=\"color:blue;\">Public Sub <\/span>TimerCallback(ByVal hWnd<span style=\"color:blue;\"> As Long<\/span>, ByVal uMsg<span style=\"color:blue;\"> As Long<\/span>, ByVal nIDEvent<span style=\"color:blue;\"> As Long<\/span>, ByVal dwTime<span style=\"color:blue;\"> As Long<\/span>)\r\n#End If\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> addinInstanz Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n        addinInstanz.BackupNotSavedModules\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 7: Der Weckruf landet im Modul und wird an die Klasse weitergereicht<\/span><\/b><\/p>\n<p>Und hier schlie&szlig;t sich der Kreis zu der Zeile aus <b>OnConnection<\/b>. Weil das Modul selbst nicht wei&szlig;, zu welcher Klasse es geh&ouml;rt, haben wir dort einen Verweis auf unsere Instanz in <b>addinInstanz<\/b> abgelegt. L&ouml;st der Wecker aus, ruft Windows die Modulprozedur auf, und diese reicht den Auftrag &uuml;ber den gemerkten Verweis an die Sicherungsroutine der Klasse weiter.<\/p>\n<h2>Sichtbar machen, ob der Helfer wacht<\/h2>\n<p>Ob die automatische Sicherung l&auml;uft, soll man auf einen Blick erkennen. Daf&uuml;r &auml;ndert <b>StatusSetzen<\/b> gleich mehrere Dinge: Der Men&uuml;eintrag bekommt ein kleines Symbol vorangestellt &#8211; einen gef&uuml;llten Kreis f&uuml;r aktiv, einen leeren f&uuml;r inaktiv &#8211; samt passendem Tooltip. Zus&auml;tzlich wandert der Zustand sogar in die Titelleiste von Access, wie Listing 8 andeutet.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>StatusSetzen(bolAktiv<span style=\"color:blue;\"> As Boolean<\/span>)\r\n    <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n    <span style=\"color:blue;\">Dim <\/span>strDB<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">On Error GoTo<\/span> Fehler\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> cbbCodeBackup Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n        <span style=\"color:blue;\">Set<\/span> db = objAccess.CurrentDb\r\n        On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n        strDB = <span style=\"color:blue;\">Mid<\/span>(db.Name, <span style=\"color:blue;\">InStrRev<\/span>(db.Name, \"\\\") + 1)\r\n        <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strDB) = 0<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;\">If <\/span>bolAktiv = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n            cbbCodeBackup.Caption = \"&#149; amvCodeBackup\"\r\n            cbbCodeBackup.TooltipText = \"Aktiv - Intervall: \" & IntervallLesen() & \"s\"\r\n            <span style=\"color:blue;\">Call<\/span> AnwendungstitelAendern(db, \"? \" & strDB & \" - Backup aktiv (\" & IntervallLesen() & \"s) \" & Now)\r\n        <span style=\"color:blue;\">Else<\/span>\r\n            cbbCodeBackup.Caption = \"o amvCodeBackup\"\r\n            cbbCodeBackup.TooltipText = \"Inaktiv\"\r\n            <span style=\"color:blue;\">Call<\/span> AnwendungstitelAendern(db, \"? \" & strDB & \" - Backup inaktiv\")\r\n        <span style=\"color:blue;\">End If<\/span>\r\n        <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> objRibbon Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n        objRibbon.InvalidateControl \"btnTimer\"\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Exit Sub<\/span>\r\nFehler:\r\n    <span style=\"color:blue;\">MsgBox<\/span> Err.Number & \" \" & Err.Description & \" \" & Erl, vbOKOnly + <span style=\"color:blue;\">vbCr<\/span>itical, \"StatusSetzen\"\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 8: StatusSetzen aktualisiert Beschriftung, Tooltip und Fenstertitel<\/span><\/b><\/p>\n<p>Das &Auml;ndern des Fensternamens &uuml;bernimmt <b>AnwendungstitelAendern<\/b>. Die Prozedur setzt die Eigenschaft <b>AppTitle<\/b> der Datenbank &#8211; und legt sie zuvor an, falls es sie noch nicht gibt, denn nicht jede Datenbank bringt diese Eigenschaft von Haus aus mit. Ein abschlie&szlig;endes <b>RefreshTitleBar<\/b> sorgt daf&uuml;r, dass die &Auml;nderung sofort sichtbar wird.<\/p>\n<h2>Das Herzst&uuml;ck: nur &Auml;nderungen sichern<\/h2>\n<p>Jetzt zum Kern des Add-Ins &#8211; jener Prozedur, die der Wecker regelm&auml;&szlig;ig ansto&szlig;t. Sie geht alle Bestandteile des Projekts durch und interessiert sich nur f&uuml;r die seit dem letzten Speichern ge&auml;nderten; das verr&auml;t die Eigenschaft <b>Saved<\/b>. F&uuml;r jedes solche Modul berechnet sie einen Fingerabdruck und vergleicht ihn mit dem zuletzt gemerkten. Nur bei einem Unterschied entsteht wirklich eine neue Datei, wie in Listing 9 zu sehen ist.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>BackupNotSavedModules()\r\n    <span style=\"color:blue;\">Dim <\/span>objVBComponent<span style=\"color:blue;\"> As <\/span>VBIDE.VBComponent, objVBProject<span style=\"color:blue;\"> As <\/span>VBIDE.VBProject, strModulename<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strModuleFolder<span style=\"color:blue;\"> As String<\/span>, strModulePath<span style=\"color:blue;\"> As String<\/span>, strAccessName<span style=\"color:blue;\"> As String<\/span>, strHashAktuell<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>bolEtwasGesichert<span style=\"color:blue;\"> As Boolean<\/span>    \r\n    <span style=\"color:blue;\">If <\/span>dicHashes Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n        <span style=\"color:blue;\">Set<\/span> dicHashes = CreateObject(\"Scripting.Dictionary\")\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Set<\/span> objVBProject = GetVBProject()\r\n    <span style=\"color:blue;\">If <\/span>objVBProject Is Nothing<span style=\"color:blue;\"> Then<\/span> <span style=\"color:blue;\">Exit Sub<\/span>\r\n    strModuleFolder = <span style=\"color:blue;\">Left<\/span>(objVBProject.FileName, <span style=\"color:blue;\">InStrRev<\/span>(objVBProject.FileName, \"\\\")) & \"BAS\"\r\n    On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n    MkDir strModuleFolder\r\n    <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n    bolEtwasGesichert = <span style=\"color:blue;\">False<\/span>\r\n    For Each objVBComponent In objVBProject.VBComponents\r\n        <span style=\"color:blue;\">If <\/span>objVBComponent.Saved = <span style=\"color:blue;\">False<\/span><span style=\"color:blue;\"> Then<\/span>\r\n            strHashAktuell = ModulHash(objVBComponent)\r\n            <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> dicHashes.Exists(objVBComponent.Name) Or dicHashes(objVBComponent.Name) &lt;&gt; strHashAktuell<span style=\"color:blue;\"> Then<\/span>\r\n                strModulename = objVBComponent.Name\r\n                strModulePath = strModuleFolder & \"\\\" & strModulename & \"_\" & Format(Now, \"YYYYMMDD_HHNNSS\") & \".bas\"\r\n                Select Case objVBComponent.Type\r\n                    <span style=\"color:blue;\">Case <\/span>vbext_ct_StdModule, vbext_ct_ClassModule\r\n                        objAccess.SaveAsText acModule, strModulename, strModulePath\r\n                    <span style=\"color:blue;\">Case <\/span>vbext_ct_Document\r\n                        strAccessName = <span style=\"color:blue;\">Mid<\/span>(strModulename, <span style=\"color:blue;\">InStr<\/span>(strModulename, \"_\") + 1)\r\n                        <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Left<\/span>(strModulename, 5) = \"Form_\"<span style=\"color:blue;\"> Then<\/span>\r\n                            objAccess.SaveAsText acForm, strAccessName, strModulePath\r\n                        <span style=\"color:blue;\">Else<\/span>If <span style=\"color:blue;\">Left<\/span>(strModulename, 7) = \"Report_\" Then\r\n                            objAccess.SaveAsText acReport, strAccessName, strModulePath\r\n                        <span style=\"color:blue;\">End If<\/span>\r\n                <span style=\"color:blue;\">End Select<\/span>\r\n                dicHashes(objVBComponent.Name) = strHashAktuell\r\n                bolEtwasGesichert = <span style=\"color:blue;\">True<\/span>\r\n                <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> cbbCodeBackup Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n                    cbbCodeBackup.TooltipText = \"Aktiv - letzter Backup: \" & strModulename & \" (\" _\r\n                        & Format(Now, \"HH:NN:SS\") & \")\"\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;\">Next<\/span> objVBComponent\r\n    <span style=\"color:blue;\">If <\/span>bolEtwasGesichert And <span style=\"color:blue;\">Not<\/span> objRibbon Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n        objRibbon.Invalidate\r\n    End If    \r\n    <span style=\"color:blue;\">Call<\/span> StatusSetzen(<span style=\"color:blue;\">True<\/span>)    \r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 9: Nur tats&auml;chlich ge&auml;nderte Module werden geschrieben<\/span><\/b><\/p>\n<p>Der Dateiname setzt sich aus dem Modulnamen und dem aktuellen Zeitpunkt zusammen. Dadurch entsteht bei jeder Sicherung eine neue Datei, und &auml;ltere Fassungen bleiben erhalten &#8211; praktisch, wenn man einmal zu einem fr&uuml;heren Stand zur&uuml;ckkehren m&ouml;chte. Je nach Art des Bestandteils w&auml;hlt der Code das richtige Verfahren: Standard- und Klassenmodule schreibt er direkt weg, bei Formularen und Berichten l&ouml;st er zuvor den eigentlichen Namen aus der Bezeichnung heraus. Zuletzt merkt er sich den neuen Fingerabdruck, damit dasselbe Modul nicht schon beim n&auml;chsten Durchgang erneut gesichert wird.<\/p>\n<h2>Der Fingerabdruck eines Moduls<\/h2>\n<p>Bleibt die Frage, wie dieser Fingerabdruck entsteht. <b>ModulHash<\/b> liest den gesamten Code eines Moduls und berechnet daraus eine kurze Pr&uuml;fsumme nach dem MD5-Verfahren. Man kann sie sich als Zahlencode vorstellen, der schon bei der kleinsten Text&auml;nderung v&ouml;llig anders ausf&auml;llt &#8211; ideal, um &Auml;nderungen zu erkennen. Den Kern zeigt Listing 10.<\/p>\n<pre><span style=\"color:blue;\">Private Function <\/span>ModulHash(objComp<span style=\"color:blue;\"> As <\/span>VBIDE.VBComponent)<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">On Error GoTo<\/span> Fallback\r\n    <span style=\"color:blue;\">Dim <\/span>strCode<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>bytData()<span style=\"color:blue;\"> As Byte<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>objMD5<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>bytHash()<span style=\"color:blue;\"> As Byte<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Integer<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strHex<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>objStream<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">With<\/span> objComp.CodeModule\r\n        <span style=\"color:blue;\">If <\/span>.CountOfLines &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n            strCode = .Lines(1, .CountOfLines)\r\n        <span style=\"color:blue;\">End If<\/span>\r\n    End <span style=\"color:blue;\">With<\/span>\r\n    <span style=\"color:blue;\">Set<\/span> objStream = CreateObject(\"ADODB.Stream\")\r\n    objStream.Open\r\n    objStream.Charset = \"utf-8\"\r\n    objStream.WriteText strCode\r\n    objStream.Position = 0\r\n    objStream.Type = 1\r\n    bytData = objStream.Read\r\n    objStream.Close\r\n    <span style=\"color:blue;\">Set<\/span> objMD5 = CreateObject(\"System.Security.Cryptography.MD5CryptoServiceProvider\")\r\n    bytHash = objMD5.ComputeHash_2(bytData)\r\n    For i = 0 To <span style=\"color:blue;\">UBound<\/span>(bytHash)\r\n        strHex = strHex & <span style=\"color:blue;\">Right<\/span>(\"00\" & Hex(bytHash(i)), 2)\r\n    <span style=\"color:blue;\">Next<\/span> i\r\n    ModulHash = strHex\r\n    <span style=\"color:blue;\">Exit Function<\/span>\r\nFallback:\r\n    ModulHash = CStr(<span style=\"color:blue;\">Len<\/span>(strCode)) & \"_\" & CStr(objComp.CodeModule.CountOfLines)\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 10: ModulHash bildet eine Pr&uuml;fsumme &uuml;ber den Modulcode<\/span><\/b><\/p>\n<p>Der Umweg &uuml;ber ein <b>ADODB.Stream<\/b>-Objekt dient allein dazu, den Text sauber in eine Folge von Bytes zu verwandeln, denn genau die erwartet die MD5-Berechnung. Geht dabei einmal etwas schief, greift ein Ersatz, der aus der L&auml;nge des Codes und der Zeilenzahl eine einfachere Kennung bildet &#8211; weniger genau, aber f&uuml;r unseren Zweck v&ouml;llig ausreichend.<\/p>\n<h2>Das Add-In bekannt machen<\/h2>\n<p>Damit Access den Helfer beim Start &uuml;berhaupt findet, muss er in der Registry stehen. Darum k&uuml;mmert sich das Modul <b>DllRegistration<\/b>. Am Anfang setzt es aus dem Projekt- und Klassennamen die Registry-Pfade zusammen:<\/p>\n<pre>Const AddinProjectName<span style=\"color:blue;\"> As String<\/span> = _\r\n    VBA.Compilation.CurrentProjectName\r\nConst AddinClassName<span style=\"color:blue;\"> As String<\/span> = \"amvCodeBackup\"\r\nConst AddinQualifiedClassName<span style=\"color:blue;\"> As String<\/span> = _\r\n    AddinProjectName & \".\" & AddinClassName\r\nConst RootRegistryFolder<span style=\"color:blue;\"> As String<\/span> = _\r\n    \"HKCU\\SOFTWARE\\Microsoft\\Office\\Access\\Addins\\\" & _\r\n    AddinQualifiedClassName & \"\\\"<\/pre>\n<p>Der Pfad zeigt in den Zweig des aktuellen Benutzers &#8211; Abk&uuml;rzung <b>HKCU<\/b> &#8211; in den Bereich der Access-Add-Ins. Weil wir dort und nicht im systemweiten Zweig schreiben, sind sp&auml;ter keine Administratorrechte n&ouml;tig. Das Eintragen selbst &uuml;bernimmt <b>DllRegisterServer<\/b> (siehe Listing 11).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>DllRegisterServer()<span style=\"color:blue;\"> As Boolean<\/span>\r\n    <span style=\"color:blue;\">On Error GoTo<\/span> RegError\r\n    <span style=\"color:blue;\">Dim <\/span>wscript<span style=\"color:blue;\"> As Object<\/span> = CreateObject(\"wscript.shell\")\r\n    wscript.RegWrite RootRegistryFolder & \"FriendlyName\", AddinProjectName, \"REG_SZ\"\r\n    wscript.RegWrite RootRegistryFolder & \"Description\", AddinProjectName, \"REG_SZ\"\r\n    wscript.RegWrite RootRegistryFolder & \"LoadBehavior\", 3, \"REG_DWORD\"\r\n    Return <span style=\"color:blue;\">True<\/span>\r\nRegError:\r\n    <span style=\"color:blue;\">MsgBox<\/span> \"DllRegisterServer -- An error occured trying to write to the system registry:\" & <span style=\"color:blue;\">vbCrLf<\/span> & _\r\n        Err.Description & \" (\" & Hex(Err.Number) & \")\"\r\n    Return <span style=\"color:blue;\">False<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 11: DllRegisterServer legt die Registry-Eintr&auml;ge an<\/span><\/b><\/p>\n<p>Die Funktion schreibt drei Werte. <b>FriendlyName<\/b> und <b>Description<\/b> erhalten den Projektnamen, und <b>LoadBehavior<\/b> bekommt die <b>3<\/b> &#8211; dieser Wert weist Access an, das Add-In gleich beim Start zu laden. Das Gegenst&uuml;ck <b>DllUnregisterServer<\/b> l&ouml;scht dieselben Eintr&auml;ge spiegelbildlich wieder, wie Listing 12 zeigt.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>DllUnregisterServer()<span style=\"color:blue;\"> As Boolean<\/span>\r\n    <span style=\"color:blue;\">On Error GoTo<\/span> RegError\r\n    <span style=\"color:blue;\">Dim <\/span>wscript<span style=\"color:blue;\"> As Object<\/span> = CreateObject(\"wscript.shell\")\r\n    wscript.RegDelete RootRegistryFolder & \"FriendlyName\"\r\n    wscript.RegDelete RootRegistryFolder & \"Description\"\r\n    wscript.RegDelete RootRegistryFolder & \"LoadBehavior\"\r\n    wscript.RegDelete RootRegistryFolder\r\n    Return <span style=\"color:blue;\">True<\/span>\r\nRegError:\r\n    <span style=\"color:blue;\">MsgBox<\/span> \"DllUnregisterServer -- An error occured trying to delete from the system registry:\" & <span style=\"color:blue;\">vbCrLf<\/span> & _\r\n        Err.Description & \" (\" & Hex(Err.Number) & \")\"\r\n    Return <span style=\"color:blue;\">False<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 12: DllUnregisterServer entfernt die Eintr&auml;ge wieder<\/span><\/b><\/p>\n<p>Die Namen <b>DllRegisterServer<\/b> und <b>DllUnregisterServer<\/b> sind nicht frei w&auml;hlbar, sondern von COM fest vorgegeben. twinBASIC ruft sie beim Registrieren und Aufheben automatisch auf; auf einem fremden Rechner &uuml;bernimmt sp&auml;ter das Windows-Werkzeug <b>regsvr32<\/b> dieselbe Aufgabe. Wichtig ist dabei nur, dass die Bitness von Werkzeug und DLL zusammenpasst.<\/p>\n<h2>Wiederherstellen gesicherter Elemente<\/h2>\n<p>Um Formlare oder Berichte wiederherzustellen, verwenden wir die <b>LoadFromText<\/b>-Methode beispielsweise wie folgt:<\/p>\n<pre>LoadFromText acform, \"frmModulSpeichern\", currentproject.Path & \"\\BAS\\Form_frmModulSpeichern_20260704_200253.bas\"<\/pre>\n<p>Zum Wiederherstellen eines Moduls ziehen wir die gesicherte Datei einfach in den Objekt-Explorer des VBA-Editors.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Aus &uuml;berschaubaren Bausteinen ist ein n&uuml;tzlicher Helfer geworden. Das COM-Add-In sichert im Hintergrund in einstellbaren Abst&auml;nden alle ge&auml;nderten Module in einzelne Textdateien &#8211; ohne Zutun des Entwicklers und dank des Fingerabdruck-Vergleichs nur dann, wenn sich wirklich etwas getan hat.<\/p>\n<p>Mit der Einstellung des Intervalls zwischen den Sicherungen w&auml;hlt man zwischen hoher Anzahl Backups und damit einer gr&ouml;&szlig;eren Sicherheit bei Access-Abst&uuml;rzen und weniger Meldungen, wenn man &Auml;nderungen an Formular- oder Berichtsmodulen durchgef&uuml;hrt hat.<\/p>\n<p>Hier taucht, wie oben bereits beschrieben, jeweils eine Meldung auf, weil ungespeicherte Formulare nicht gesichert werden k&ouml;nnen.<\/p>\n<p>Man k&ouml;nnte nun argumentieren, dass eine Sicherung von Formularen und Berichten nicht mehr n&ouml;tig ist, wenn man diese ohnehin vor der Sicherung speichern muss.<\/p>\n<p>Allerdings erhalten wir damit einen bisher noch nicht erw&auml;hnten Vorteil: Wir sichern so den Zustand aller ge&auml;nderten Module in einzelnen Dateien. So k&ouml;nnen wir, wenn keine andere Versionsverwaltung im Einsatz ist, auch Zwischenst&auml;nde wiederherstellen, wenn wir beim Entwickeln einmal in eine Sackgasse gelaufen sind. Bild 4 zeigt die im BAS-Ordner gesicherten Versionen verschiedener Module.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_04\/pic_1612_003.png\" alt=\"Gesicherte Formulare und Module im Windows Explorer\" width=\"700\" height=\"359,2195\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Gesicherte Formulare und Module im Windows Explorer<\/span><\/b><\/p>\n<p>Zwei Kniffe bleiben besonders im Ged&auml;chtnis. Da ist der Umweg des Weckrufs &uuml;ber ein eigenes Modul, weil Windows beim Timer keine Klassenmethode direkt ansprechen kann. Und da ist der Fingerabdruck per MD5, der unn&ouml;tige Sicherungen unver&auml;nderter Module verhindert. Wer m&ouml;chte, baut den Helfer leicht aus &#8211; etwa um eine Obergrenze f&uuml;r die Zahl gespeicherter Fassungen je Modul oder um eine Schaltfl&auml;che, die den BAS-Ordner direkt &ouml;ffnet.<\/p>\n<p>Die gesicherten Module m&uuml;ssen momentan noch manuell hinzugef&uuml;gt werden &#8211; man k&ouml;nnte noch eine M&ouml;glichkeit schaffen, diese &uuml;bersichtlich darzustellen und sie zum Wiederherstellen per Mausklick einfach anzuklicken.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wer viel in VBA programmiert, kennt den Schreck: Ein Absturz von Access, und die Arbeit der letzten halben Stunde ist weg. Nat&uuml;rlich kann man von Hand speichern &#8211; aber genau das vergisst man im Eifer des Gefechts. In diesem Beitrag bauen wir uns einen flei&szlig;igen Helfer, der uns diese Sorge abnimmt: ein COM-Add-In f&uuml;r den Access-Entwickler, das in einstellbaren Abst&auml;nden alle ge&auml;nderten Formulare, Berichte und Module automatisch in einzelne Textdateien sichert. So haben wir immer eine frische Kopie unseres Codes zur Hand, ganz ohne daran zu denken.<\/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":[662026,66042026,44000027],"tags":[],"class_list":["post-55001612","post","type-post","status-publish","format-standard","hentry","category-662026","category-66042026","category-Loesungen"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>VBA-Module regelm&auml;&szlig;ig sichern per COM-Add-In - Access im Unternehmen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"VBA-Module regelm&auml;&szlig;ig sichern per COM-Add-In\" \/>\n<meta property=\"og:description\" content=\"Wer viel in VBA programmiert, kennt den Schreck: Ein Absturz von Access, und die Arbeit der letzten halben Stunde ist weg. Nat&uuml;rlich kann man von Hand speichern - aber genau das vergisst man im Eifer des Gefechts. In diesem Beitrag bauen wir uns einen flei&szlig;igen Helfer, der uns diese Sorge abnimmt: ein COM-Add-In f&uuml;r den Access-Entwickler, das in einstellbaren Abst&auml;nden alle ge&auml;nderten Formulare, Berichte und Module automatisch in einzelne Textdateien sichert. So haben wir immer eine frische Kopie unseres Codes zur Hand, ganz ohne daran zu denken.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2026-07-05T11:03:27+00:00\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Minhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Minhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"25\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/VBAModule_regelmaessig_sichern_per_COMAddIn\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/VBAModule_regelmaessig_sichern_per_COMAddIn\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"VBA-Module regelm&auml;&szlig;ig sichern per COM-Add-In\",\"datePublished\":\"2026-07-05T11:03:27+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/VBAModule_regelmaessig_sichern_per_COMAddIn\\\/\"},\"wordCount\":3314,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"articleSection\":[\"2026\",\"4\\\/2026\",\"L\u00f6sungen\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/VBAModule_regelmaessig_sichern_per_COMAddIn\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/VBAModule_regelmaessig_sichern_per_COMAddIn\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/VBAModule_regelmaessig_sichern_per_COMAddIn\\\/\",\"name\":\"VBA-Module regelm&auml;&szlig;ig sichern per COM-Add-In - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"datePublished\":\"2026-07-05T11:03:27+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/VBAModule_regelmaessig_sichern_per_COMAddIn\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/VBAModule_regelmaessig_sichern_per_COMAddIn\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/VBAModule_regelmaessig_sichern_per_COMAddIn\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"VBA-Module regelm&auml;&szlig;ig sichern per COM-Add-In\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"name\":\"Access im Unternehmen\",\"description\":\"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access\",\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/access-im-unternehmen.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\",\"name\":\"Andr\u00e9 Minhorst Verlag\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"contentUrl\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"width\":370,\"height\":111,\"caption\":\"Andr\u00e9 Minhorst Verlag\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\",\"name\":\"Andr\u00e9 Minhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"caption\":\"Andr\u00e9 Minhorst\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"VBA-Module regelm&auml;&szlig;ig sichern per COM-Add-In - Access im Unternehmen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/","og_locale":"de_DE","og_type":"article","og_title":"VBA-Module regelm&auml;&szlig;ig sichern per COM-Add-In","og_description":"Wer viel in VBA programmiert, kennt den Schreck: Ein Absturz von Access, und die Arbeit der letzten halben Stunde ist weg. Nat&uuml;rlich kann man von Hand speichern - aber genau das vergisst man im Eifer des Gefechts. In diesem Beitrag bauen wir uns einen flei&szlig;igen Helfer, der uns diese Sorge abnimmt: ein COM-Add-In f&uuml;r den Access-Entwickler, das in einstellbaren Abst&auml;nden alle ge&auml;nderten Formulare, Berichte und Module automatisch in einzelne Textdateien sichert. So haben wir immer eine frische Kopie unseres Codes zur Hand, ganz ohne daran zu denken.","og_url":"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/","og_site_name":"Access im Unternehmen","article_published_time":"2026-07-05T11:03:27+00:00","author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"25\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"VBA-Module regelm&auml;&szlig;ig sichern per COM-Add-In","datePublished":"2026-07-05T11:03:27+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/"},"wordCount":3314,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"articleSection":["2026","4\/2026","L\u00f6sungen"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/","url":"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/","name":"VBA-Module regelm&auml;&szlig;ig sichern per COM-Add-In - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"datePublished":"2026-07-05T11:03:27+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/VBAModule_regelmaessig_sichern_per_COMAddIn\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"VBA-Module regelm&auml;&szlig;ig sichern per COM-Add-In"}]},{"@type":"WebSite","@id":"https:\/\/access-im-unternehmen.de\/#website","url":"https:\/\/access-im-unternehmen.de\/","name":"Access im Unternehmen","description":"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access","publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/access-im-unternehmen.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/access-im-unternehmen.de\/#organization","name":"Andr\u00e9 Minhorst Verlag","url":"https:\/\/access-im-unternehmen.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/","url":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","contentUrl":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","width":370,"height":111,"caption":"Andr\u00e9 Minhorst Verlag"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f","name":"Andr\u00e9 Minhorst","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","caption":"Andr\u00e9 Minhorst"}}]}},"_links":{"self":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001612","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/comments?post=55001612"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001612\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001612"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001612"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001612"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}