{"id":55000610,"date":"2008-06-01T00:00:00","date_gmt":"2020-05-11T11:50:33","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=610"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"QuellcodeVersionsverwaltung_inside","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/","title":{"rendered":"Quellcode-Versionsverwaltung inside"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg09.met.vgwort.de\/na\/42efa9541fb745e9b2f3fefd1118e83b\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>So, jetzt habe ich endg&uuml;ltig die Nase voll. Schon wieder ist es passiert: Mit letzter Kraft eine wichtige Routine zu Ende programmiert und jetzt nur noch Access schlie&szlig;en, den Rechner runterfahren und ab ins Bett. Und am n&auml;chsten Morgen die Ern&uuml;chterung: Die &auml;nderungen sind nicht mehr da! Da habe ich wohl mal wieder mit &#8222;Nein&#8220; auf die Frage geantwortet, ob ich die ge&auml;nderten Objekte speichern m&ouml;chte &#8230; Aber damit ist jetzt Schluss: Ich baue mir ein Tool, das regelm&auml;&szlig;ig meine Formulare, Berichte und Module speichert &#8211; und dabei auch noch alte Versionen aufbewahrt.<\/b><\/p>\n<p>Es ist ein Kreuz: Es braucht unter Access nur ein wenig Ungeschicklichkeit, um eine Menge Arbeit zu vernichten. Wenn Sie mal ein umfangreiches Modul programmiert und dann die Datenbank oder das Modul geschlossen haben, ohne den neuen Stand zu speichern, wissen Sie, wovon ich spreche. Zwischenspeichern <\/p>\n<p>Wenn man gerade im Programmierrausch ist und eine Zeile nach der anderen schreibt, denkt man nicht unbedingt an das Zwischenspeichern, auch wenn es doch so einfach w&auml;re, zwischendurch einfach mal <b>Strg + S <\/b>zu dr&uuml;cken.<\/p>\n<p>Wie kommt man dem bei Termine in Outlook setzen, die einen alle 15 Minuten daran erinnern, mal durchzupusten und den aktuellen Stand zu speichern<\/p>\n<p>So weit kommt es noch &#8211; wir sind schlie&szlig;lich Programmierer und die helfen sich selbst.<\/p>\n<p>Wenn man sich aber schon an die Programmierung eines Tools macht, das regelm&auml;&szlig;ig f&uuml;r das Speichern ge&auml;nderter Objekte sorgt, was sollte dieses Tool praktischerweise noch erledigen<\/p>\n<p>Das automatische Speichern ist nicht unbedingt immer eine perfekte L&ouml;sung: Immerhin zerst&ouml;rt man damit ja auch die M&ouml;glichkeit, zum Stand der letzten Speicherung zur&uuml;ckzukehren &#8211; und damit zur m&ouml;glicherweise letzten funktionst&uuml;chtigen Version.<\/p>\n<p>Unter Access und speziell im VBA-Editor ist das praktisch eine Lebensversicherung, wenn man mal gr&ouml;&szlig;ere &auml;nderungen durchf&uuml;hrt und diese dann doch nicht &uuml;bernehmen m&ouml;chte &#8211; man speichert sie dann einfach nicht.<\/p>\n<p>Der VBA-Editor bietet n&auml;mlich auch in der aktuellen Version nur die M&ouml;glichkeit, 20 Schritte r&uuml;ckg&auml;ngig zu machen &#8211; insgesamt, nicht pro Modul!<\/p>\n<p>Es scheint also eine gute Idee, die gespeicherten Zwischenst&auml;nde zu sichern. Aber wohin damit &#8211; und wie<\/p>\n<p>Bevor wir uns diesen technischen Feinheiten zuwenden, halten wir fest: Access soll den Stand der Entwicklung in regelm&auml;&szlig;igen Abst&auml;nden, zumindest aber beim Schlie&szlig;en der Anwendung, sichern.<\/p>\n<p>Auf diese Weise sorgen Sie nicht nur daf&uuml;r, dass &auml;nderungen nicht verloren gehen, sondern k&ouml;nnen auch noch auf fr&uuml;here St&auml;nde zur&uuml;ckgreifen.<\/p>\n<h2>Anwendung<\/h2>\n<p>Schauen wir uns an, wie das Ganze funktioniert: Nachdem Sie die Elemente der Anwendung wie im Kasten &#8222;Im Soforteinsatz&#8220; zu einer Datenbank hinzugef&uuml;gt und daf&uuml;r gesorgt haben, dass das Formular <b>frmAccVersion <\/b>angezeigt wird, k&ouml;nnen Sie schon loslegen.<\/p>\n<p>Das Formular sehen Sie in Bild 1. Es enth&auml;lt ein Listenfeld zur Anzeige der Module und ein weiteres zur Auflistung der Versionen zum aktuell im ersten Listenfeld markierten Modul. Zu den Modulen geh&ouml;ren in dem Fall Formulare, Berichte, Standardmodule und Klassenmodule, aber auch Abfragen.<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2008_02\/Versionsverwaltung-web-images\/pic001_opt.jpeg\" alt=\"pic001.tif\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 1: Das Formular zur Verwaltung der Versionen<\/span><\/b><\/p>\n<p>Beim ersten &Ouml;ffnen des Formulars sind diese Listenfelder nat&uuml;rlich noch leer, was sich aber nach einem Klick auf die Schaltfl&auml;che <b>Neue Version aller ge&auml;nderten Objekte speichern <\/b>&auml;ndert. Die Versionsverwaltung durchl&auml;uft dann alle Objekte der genannten Typen und legt zun&auml;chst das Modul im ersten Listenfeld und dann die Version im zweiten Listenfeld an.<\/p>\n<p>Damit hat man schon einmal einen Zwischenstand, mit dem man sich ganz unbeschwert an den Code heranwagen kann, denn mit der Schaltfl&auml;che <b>Bestehendes Objekt mit dieser Version &uuml;berschreiben <\/b>k&ouml;nnen Sie sich ganz leicht eine &auml;ltere Version zur&uuml;ckholen.<\/p>\n<p>Falls Sie sich nicht sicher sind, welche Version die richtige ist, stellen Sie einfach die Version wieder her, hinter der Sie die richtige vermuten &#8211; und zwar mit der Schaltfl&auml;che <b>Objekt wiederherstellen als &lt;Name&gt;_yyyymmdd_hhnnss<\/b>, die bestehende Objekte nicht &uuml;berschreibt, sondern unter einem Namen anlegt, der den Zeitpunkt der Speicherung enth&auml;lt.<\/p>\n<p>Die Schaltfl&auml;che <b>Version l&ouml;schen <\/b>dient schlie&szlig;lich dazu, eine oder mehrere Versionen zu entsorgen &#8211; wenn man einen Versionsstand erreicht hat, bei dem alles funktioniert, braucht man selbstverst&auml;ndlich keine Altlasten mehr mit sich herumzutragen.<\/p>\n<p>Schlie&szlig;lich gibt es im unteren Bereich noch die Steuerelemente zum Festlegen der regelm&auml;&szlig;igen Sicherung von Versionsst&auml;nden. Mit dem Kontrollk&auml;stchen <b>Ge&auml;nderte Objekte alle x Minuten als neue Version sichern <\/b>aktivieren Sie diese Funktion, in das Textfeld <b>Intervall [min] <\/b>tragen Sie ein, in welchem Abstand die Sicherung erfolgen soll.<\/p>\n<h2>Schlie&szlig;en ohne zu speichern<\/h2>\n<p>Wenn der Benutzer die Datenbankanwendung beendet, schlie&szlig;t er auch das Formular <b>frmAccVersion<\/b>. Dieses sorgt dann mit seiner letzten Aktion noch einmal daf&uuml;r, dass nicht gespeicherte Module in den Versionierungstabellen gespeichert werden.<\/p>\n<p>Wenn Sie also ein Modul namens <b>mdlTest <\/b>bearbeiten, die &auml;nderungen nicht speichern und dann die Anwendung schlie&szlig;en, speichert <b>frmAccVersion <\/b>trotzdem die aktuelle Version des Moduls.<\/p>\n<p>Beim n&auml;chsten &Ouml;ffnen der Datenbank erscheint eine Meldung, die den Benutzer darauf hinweist, dass es Module oder Objekte gibt, die vor dem letzten Schlie&szlig;en ge&auml;ndert, aber nicht gespeichert wurden (siehe Bild 2).<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2008_02\/Versionsverwaltung-web-images\/pic004_opt.jpeg\" alt=\"pic004.tif\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 2: Diese Meldung macht den Benutzer auf ungespeicherte Elemente aufmerksam.<\/span><\/b><\/p>\n<p>Im Anschluss erscheint das Formular <b>frmAccVersion<\/b> und zeigt das nicht gespeicherte Objekt unter dem Originalobjekt an &#8211; versehen mit einer GUID. Mit einem Klick auf die Schaltfl&auml;che <b>Objekte wiederherstellen als &lt;Name&gt;_yyyymmdd_hhnnss <\/b>erzeugen Sie dann eine Kopie des Objekts mit dem ungespeicherten Stand beim letzten Schlie&szlig;en der Anwendung (siehe Bild 3).<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2008_02\/Versionsverwaltung-web-images\/pic005_opt.jpeg\" alt=\"pic005.tif\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 3: Nicht gespeicherte &auml;nderungen lassen sich mit AccVersion zur&uuml;ckholen.<\/span><\/b><\/p>\n<h2>Nebenwirkungen<\/h2>\n<p>F&uuml;r manche Objekte ist es wichtig, diese vor dem Versionieren zu speichern. Dies gilt f&uuml;r Abfragen, Berichte und Formulare. Standard- und Klassenmodule stehen ohne weitere Schritte zum Versionieren bereit.<\/p>\n<p>Das ist allerdings ein Problem, denn dieses Tool soll ja gerade auch dann, wenn der Benutzer es vers&auml;umt, ein Objekt vor dem Beenden der Datenbank zu speichern, geradestehen und die seit dem letzten Speichern get&auml;tigten &auml;nderungen sichern.<\/p>\n<p>Ganz einfach ist das allerdings nicht, ohne den Benutzer nicht doch noch zu einem Klick auf die <b>Ja<\/b>-Schaltfl&auml;che der <b>Speichern<\/b>-Frage zu n&ouml;tigen &#8211; warum, erfahren Sie weiter unten.<\/p>\n<h2>Datenmodell<\/h2>\n<p>Die Anwendung braucht genau zwei Tabellen: Eine zum Speichern der Informationen zu den Modulen selbst und eine f&uuml;r die einzelnen Versionen der Module. Bild 4 zeigt die Tabellen und ihre Beziehung.<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2008_02\/Versionsverwaltung-web-images\/pic002_opt.jpeg\" alt=\"pic002.tif\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 4: Datenmodell f&uuml;r die Verwaltung von Modulen und deren Versionen<\/span><\/b><\/p>\n<p>Die Tabelle <b>tblModule <\/b>enth&auml;lt neben dem Feld <b>Modulname <\/b>mit der Bezeichnung des Moduls nur noch ein Feld namens <b>Modultyp<\/b>. Dieses enth&auml;lt einen Zahlenwert, der einer der Konstanten der VBA-Enumeration <b>acObjectType <\/b>entspricht. In diesem Zusammenhang kommen die folgenden Typen vor (Zahlenwert in Klammern):<\/p>\n<ul>\n<li class=\"aufz-hlung\"><b>acQuery <\/b>(1)<\/li>\n<li class=\"aufz-hlung\"><b>acForm <\/b>(2)<\/li>\n<li class=\"aufz-hlung\"><b>acReport <\/b>(3)<\/li>\n<li class=\"aufz-hlung\"><b>acModule <\/b>(5)<\/li>\n<\/ul>\n<p>Unter <b>acModule <\/b>fallen dabei sowohl Klassen- als auch Standardmodule.<\/p>\n<p>Die zweite Tabelle hei&szlig;t <b>tblVersionen <\/b>und ist &uuml;ber ein Fremdschl&uuml;sselfeld namens <b>ModulID <\/b>mit der Tabelle <b>tblModule <\/b>verkn&uuml;pft. Auf diese Weise k&ouml;nnen die Datens&auml;tze dieser Tabelle den Modulen aus der Tabelle <b>tblModule <\/b>zugeordnet werden.<\/p>\n<p>Die Tabelle enth&auml;lt weitere Felder zum Speichern des eigentlichen Inhalts des Moduls (<b>Modulinhalt<\/b>) und des Speicherdatums (<b>Speicherdatum<\/b>).<\/p>\n<p>Das Feld <b>Modulinhalt <\/b>ist dabei als Memofeld ausgelegt, weil es sehr lange Texte erfassen k&ouml;nnen muss.<\/p>\n<h2>Datenmodell automatisch erstellen<\/h2>\n<p>Der Benutzer soll so wenige Objekte wie m&ouml;glich in seine Anwendung importieren m&uuml;ssen, um mit der Versionsverwaltung arbeiten zu k&ouml;nnen. Daher soll das Hauptformular von <b>AccVersion <\/b>&#8211; so soll das Tool hei&szlig;en &#8211; beim Laden pr&uuml;fen, ob die ben&ouml;tigten Tabellen schon vorhanden sind und diese anderenfalls anlegen. Ist das der Fall, kommt die Prozedur aus Listing 1 zum Einsatz.<\/p>\n<p class=\"kastentabelleheader\">Listing 1: Anlegen der f&uuml;r die Versionsverwaltung ben&ouml;tigten Tabellen per SQL<\/p>\n<pre>Public Sub TabellenAnlegen()\r\nDim cnn As Object\r\nSet cnn = CurrentProject.Connection\r\ncnn.Execute &quot;CREATE TABLE tblModule(ModulID COUNTER CONSTRAINT PK PRIMARY KEY, &quot; _\r\n&amp; &quot;Modulname VARCHAR(255), Modultyp INT)&quot;, dbFailOnError\r\ncnn.Execute &quot;CREATE TABLE tblVersionen(VersionID COUNTER CONSTRAINT PK PRIMARY KEY, &quot; _\r\n&amp; &quot;Modulinhalt LONGTEXT, Speicherdatum DATETIME, ModulID INT)&quot;, dbFailOnError\r\ncnn.Execute &quot;ALTER TABLE tblVersionen ADD CONSTRAINT FKModulID FOREIGN KEY (ModulID) &quot; _\r\n&amp; &quot;REFERENCES tblModule ON DELETE CASCADE ON UPDATE CASCADE&quot;, dbFailOnError\r\nApplication.RefreshDatabaseWindow\r\nEnd Sub<\/pre>\n<p>Diese Routine erzeugt zun&auml;chst die Tabelle <b>tblModule<\/b>, dann <b>tblVersionen <\/b>und schlie&szlig;lich das Fremdschl&uuml;sselfeld mit referentieller Integrit&auml;t sowie L&ouml;sch- und Aktualisierungsweitergabe.<\/p>\n<h2>Tabelleninhalt<\/h2>\n<p>Der Inhalt der meisten Felder der Tabellen des Datenmodells bedarf keiner Erkl&auml;rung, das Feld <b>Modulinhalt<\/b> aber sehr wohl. Wer sich ein Modul (sei es ein einfaches Klassenmodul, ein Standardmodul oder auch das Klassenmodul eines Formulars oder Berichts) einmal angesehen hat, sieht zun&auml;chst nur den darin enthaltenen Code.<\/p>\n<p>Nur diesen zu speichern, w&uuml;rde zwar schon weiterhelfen, denn somit k&ouml;nnten Sie zumindest den Verlust umfangreicher Code&auml;nderungen verhindern.<\/p>\n<p>Wer aber einmal ein Formular oder einen Bericht mit einer Menge Steuerelemente und entsprechenden Anpassungen der Eigenschaften gebaut hat und sp&auml;ter alles noch einmal neu bauen musste, weil er im falschen Moment die falsche Schaltfl&auml;che angeklickt hat, wird sich &uuml;ber Folgendes freuen: Auch die Definition s&auml;mtlicher Eigenschaften von Formularen und Berichten und den darin enthaltenen Steuerelementen liegt in Textform vor und Sie k&ouml;nnen sogar darauf zugreifen!<\/p>\n<p>Nun ist es nicht so, als ob diese Informationen offen herumliegen, aber &uuml;ber einen Zwischenschritt k&ouml;nnen Sie die komplette Definition von Objekten wie Formularen oder Berichten im Feld <b>Modulinhalt <\/b>der Tabelle <b>tblVersionen <\/b>speichern.<\/p>\n<p>Dies macht die verborgene Methode <b>SaveAsText <\/b>von <b>Access.Application<\/b> m&ouml;glich, die drei Parameter erwartet:<\/p>\n<ul>\n<li class=\"aufz-hlung\"><b>ObjectType<\/b>: Typ des Objekts, entspricht einem Wert der weiter oben schon erw&auml;hnten VBA-Enumeration <b>acObjectType<\/b><\/li>\n<li class=\"aufz-hlung\"><b>ObjectName<\/b>: Name des Objekts, also beispielsweise <b>frmBeispielformular<\/b><\/li>\n<li class=\"aufz-hlung\"><b>Filename<\/b>: Speicherort- und Name, also etwa <b>c:\\frmBeispielformular.txt<\/b><\/li>\n<\/ul>\n<p>Wenn Sie mit dieser Methode ein noch leeres Formular als Textdatei speichern und es anschlie&szlig;end in einem Texteditor ansehen, sieht das beispielsweise wie in Bild 5 aus.<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2008_02\/Versionsverwaltung-web-images\/pic003_opt.jpeg\" alt=\"pic003.tif\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 5: Die Definition eines leeren Formulars als Textdatei<\/span><\/b><\/p>\n<h2>Versionieren von Access-Objekten<\/h2>\n<p>Der schwierigste Teil von <b>AccVersion <\/b>ist das eigentliche Versionieren der Module.<\/p>\n<p>Dabei durchl&auml;uft eine Routine alle Elemente der Anwendung, die einem der m&ouml;glichen Typen entsprechen und unterzieht diese einer ganzen Reihe von Methoden.<\/p>\n<p>Wie aufwendig das ist, verdeutlicht ein Blick auf das Flussdiagramm aus Bild 6, dessen einzelne Schritte wir in den n&auml;chsten Abschnitten genau beleuchten werden.<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2008_02\/Versionsverwaltung-web-images\/Flussdiagramm_opt.jpeg\" alt=\"Flussdiagramm.emf\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 6: Ablauf beim Versionieren der Objekte einer Access-Anwendung<\/span><\/b><\/p>\n<p>Der Hauptteil dieses Flussdiagramms l&auml;uft in der Routine <b>ModuleSpeichern <\/b>ab (s. Listing 2). Von dort aus werden noch eine Reihe weiterer Routinen aufgerufen. Den Rahmen bildet eine <b>Do While<\/b>-Schleife, die alle Datens&auml;tze der Tabelle <b>MSysObjects <\/b>durchl&auml;uft, die speziellen Anforderungen gen&uuml;gen.<\/p>\n<p class=\"kastentabelleheader\">Listing 2: Diese Routine steuert das Versionieren der Module.<\/p>\n<pre>Public Function ModuleSpeichern(bolZwischenspeichern As Boolean) As Boolean\r\n    ...\r\n    Set db = CurrentDb\r\n    strSQL = &quot;SELECT Name, Type FROM MSysObjects WHERE Type=1 Or Type=5 Or Type=8 Or Type=-32761 &quot; _\r\n    &amp; &quot;Or Type=-32764 Or Type=-32768;&quot;\r\n    Set rst = db.OpenRecordset(strSQL, dbOpenDynaset)\r\n    FortschrittAnzeigen\r\n    Do While Not rst.EOF\r\n        If Not (Left(rst!Name, 2) = &quot;__&quot; Or Left(rst!Name, 1) = &quot;~&quot;) Then\r\n            strModulname = rst!Name\r\n            Select Case rst!Type\r\n            Case -32761 &apos;Modul\r\n            lngObjecttype = acModule\r\n            Case -32764 &apos;Report\r\n            lngObjecttype = acReport\r\n            Case -32768 &apos;Form\r\n            lngObjecttype = acForm\r\n            Case 5\r\n            lngObjecttype = acQuery\r\n            Case Else &apos;andere Objekte\r\n            strModulname = &quot;&quot;\r\n            End Select\r\n            If Not Len(strModulname) = 0 Then\r\n                FortschrittAktualisieren &quot;Versioniere &quot; &amp; rst!Name, _\r\n                100 * rst.AbsolutePosition \/ rst.RecordCount\r\n                strModulname_Original = strModulname\r\n                If SaveModule(strModulname, lngObjecttype, bolZwischenspeichern) = True Then\r\n                    strModulinhalt = LoadModule\r\n                    If MustSaveObject(strModulname) Then\r\n                        SaveObject strModulname, lngObjecttype\r\n                    End If\r\n                    lngModulID = DLookup(&quot;ModulID&quot;, &quot;tblModule&quot;, &quot;Modulname=&apos;&quot; &amp; strModulname &amp; &quot;&apos;&quot;)\r\n                    If MustSaveVersion(lngModulID, strModulinhalt) Then\r\n                        MakeNewVersion lngModulID, strModulinhalt\r\n                    End If\r\n                    TemporaereObjekteLoeschen\r\n                End If\r\n            End If\r\n        End If\r\n        rst.MoveNext\r\n    Loop\r\n    FortschrittBeenden\r\nEnd Function<\/pre>\n<p><!--30percent--><\/p>\n<p>Diese Tabelle enth&auml;lt Informationen &uuml;ber alle Objekte der Datenbank &#8211; Tabellen, Abfragen, Formulare, Berichte, Module, Indizes, in Formulare, Berichte und Steuerelemente eingebettete Abfragen und mehr. Diese Tabelle interessiert uns, weil sie zuverl&auml;ssig den Namen und den Typ der gew&uuml;nschten Access-Objekte liefert. Die f&uuml;r <b>strSQL <\/b>angegebenen Kriterien entsprechen je einer Objektart, allerdings sind die Zahlenwerte nicht mit denen der VBA-Enumeration <b>acObjectType <\/b>identisch.<\/p>\n<p>Nachfolgend wird durch Zahlen in Klammern auf die einzelnen Stationen des Flussdiagramms verwiesen.<\/p>\n<p>Der obere Teil der Routine aus Listing 2 besch&auml;ftigt sich mit dem Ermitteln der betroffenen Objekte und enth&auml;lt au&szlig;erdem den Start der <b>Do While<\/b>-Schleife, die alle Datens&auml;tze der oben erw&auml;hnten Abfrage durchl&auml;uft (1). Direkt im Anschluss pr&uuml;ft eine <b>If&#8230;Then<\/b>-Bedingung einige weitere Eigenschaften, so etwa, ob der Objektname mit zwei Unterstrichen (__) oder mit der Tilde (~) beginnt.<\/p>\n<p>Ersteres ist eine Kennzeichnung f&uuml;r tempor&auml;re und zum Betrachten gedachte Objekte (mehr dazu weiter unten), Letzteres das erste Zeichen spezieller und hier nicht ber&uuml;cksichtigter Access-Objekte wie etwa die in Daten- und Datensatzherk&uuml;nften gespeicherten Abfragen (2).<\/p>\n<p>Der Objektname wird dann in der Variablen <b>strModulname <\/b>und der Objekttyp in <b>lngObjectType <\/b>gespeichert, wobei Letzteres in einer <b>Select Case<\/b>-Anweisung geschieht, welche die Objekt-IDs aus der Tabelle <b>MSysObjects <\/b>in die der Enumeration <b>acObjectType <\/b>&uuml;bersetzt.<\/p>\n<p>Den Modulnamen speichert die Routine dann sicherheitshalber in einer weiteren Variablen namens <b>strModulname_Original <\/b>zwischen, dieser Wert wird weiter unten noch ben&ouml;tigt. <\/p>\n<p>Dann folgt die erste externe Funktion, n&auml;mlich <b>SaveModule<\/b> (s. Listing 3). Im ersten Schritt (3) versucht diese Routine, den Inhalt des aktuellen Objekts als Textdatei zu speichern. Dies gelingt gerade bei Formularen und Berichten nicht unbedingt: Wenn diese sich in einem ungespeicherten Zustand befinden, kann man sie nicht so einfach mit <b>SaveAsText<\/b> auf die Festplatte bannen. Im schlechtesten Fall l&ouml;st dies einen Fehler aus, der aber in der folgenden <b>Select Case<\/b>-Anweisung behandelt wird.<\/p>\n<p class=\"kastentabelleheader\">Listing 3: Diese Routine pr&uuml;ft, ob ein Modul aktuell gespeichert ist.<\/p>\n<pre>Public Function SaveModule(strModulname As String, lngObjecttype As AcObjectType, _\r\n    bolZwischenspeichern As Boolean) As Boolean\r\n    On Error Resume Next\r\n    Dim strGuid As String\r\n    strGuid = CreateGUID\r\n    SaveAsText lngObjecttype, strModulname, CurrentProject.Path &amp; &quot;\\temp.txt&quot;\r\n    Select Case Err.Number\r\n    Case 32584, 2001\r\n    If Not bolZwischenspeichern Then\r\n        On Error GoTo 0\r\n        DoCmd.SelectObject lngObjecttype, strModulname, True\r\n        On Error Resume Next\r\n        DoCmd.CopyObject , strModulname &amp; &quot;_&quot; &amp; strGuid, lngObjecttype, strModulname\r\n        On Error GoTo 0\r\n        SaveAsText lngObjecttype, strModulname &amp; &quot;_&quot; &amp; strGuid, _\r\n        CurrentProject.Path &amp; &quot;\\temp.txt&quot;\r\n        strModulname = strModulname &amp; &quot;_&quot; &amp; strGuid\r\n        SaveModule = True\r\n    Else\r\n        SaveModule = False\r\n    End If\r\n    Case 0\r\n    SaveModule = True\r\n    Case Else\r\n    SaveModule = False\r\n    MsgBox &quot;Fehler &quot; &amp; Err.Number &amp; &quot; &quot; &amp; Err.Description\r\n    End Select\r\n    If lngObjecttype = acModule Then\r\n        If VBE.ActiveVBProject.VBComponents.Item(strModulname).Saved = False Then\r\n            strModulname = strModulname &amp; &quot;_&quot; &amp; strGuid\r\n        End If\r\n    End If\r\nEnd Function<\/pre>\n<p>Ungespeicherter Zustand bedeutet, dass ein Objekt zwar ge&auml;ndert (egal, ob diese &auml;nderung sich auf den Code oder auch auf Steuerelemente et cetera bezieht), aber seit dieser &auml;nderung noch nicht gespeichert wurde.<\/p>\n<p>Die Verhaltensweise ist bei Abfragen, Formularen und Berichten gegen&uuml;ber Standard- und einfachen Klassenmodulen unterschiedlich: Bei Ersteren l&auml;sst sich ohne Speicherung gar kein Speichern in eine Textdatei per <b>SaveAsText <\/b>bewerkstelligen, bei Letzteren hingegen schon: Dort speichert <b>SaveAsText <\/b>ohne Probleme den aktuellen Stand in eine Textdatei, ohne dass man zuvor auf den Speichern-Knopf der VBA-Entwicklungsumgebung klicken m&uuml;sste.<\/p>\n<p>Dementsprechend treten die in der <b>Select Case<\/b>-Anweisung (4) behandelten Fehler <b>32584 <\/b>und <b>2001 <\/b>auch nur in Zusammenhang mit Abfragen, Formularen und Berichten auf.<\/p>\n<p>Ist dies der Fall, geschieht Folgendes (5): Wenn das Objekt sich nicht ungespeichert per <b>SaveAsText <\/b>auf die Festplatte bannen l&auml;sst, kopieren wir das Objekt einfach in ein tempor&auml;res Objekt, speichern dieses und schreiben es dann ganz einfach in eine Textdatei.<\/p>\n<p>Einziges Problem: Wir haben jetzt eine Dublette des eigentlichen Objekts und m&uuml;ssen mit dieser weiterarbeiten. Daher weist die Routine dem &Uuml;bergabeparameter <b>strModulname <\/b>den neuen Modulnamen zu, der aus dem alten Modulnamen plus einer GUID besteht &#8211; eine GUID ist eine mit hoher Wahrscheinlichkeit eindeutige Zeichenfolge.<\/p>\n<p>Auch bei Standard- oder Klassenmodulen kann es vorkommen, dass diese zwar ge&auml;ndert, aber nicht gespeichert sind. Dies wirkt sich nicht auf <b>SaveAsText <\/b>aus: Es speichert immer den aktuellen Stand, der damit nicht unbedingt dem in der Datenbank gespeicherten Zustand entsprechen muss.<\/p>\n<p>Daher pr&uuml;ft die Routine <b>SaveModule <\/b>auch bei solchen Objekten den Gespeichert-Zustand, was hier allerdings viel einfacher &uuml;ber eine Eigenschaft namens <b>Saved <\/b>geschieht. Ist die Routine noch nicht gespeichert, wird auch hier der Modulname mit anh&auml;ngender GUID statt des einfachen Modulnamens zur&uuml;ckgegeben.<\/p>\n<p>Warum das ganze Theater mit den ver&auml;nderten Modulnamen f&uuml;r nicht gespeicherte Objekte <\/p>\n<p>Ganz einfach: Eines der Ziele von <b>AccVersion <\/b>ist es ja gerade, auch nicht gespeicherte &auml;nderungen zu versionieren, also in einem eigenen Datensatz der Tabelle <b>tblVersionen <\/b>zu speichern. Wenn so etwas geschieht &#8211; also der Anwender &auml;nderungen an einem Modul vornimmt, ohne diese beim Schlie&szlig;en der Anwendung zu speichern &#8211; soll er, &auml;hnlich wie bei Word, darauf aufmerksam gemacht werden.<\/p>\n<p>Und damit die aufrufende Routine <b>ModulSpeichern <\/b>erkennt, dass es sich um einen solchen Fall handelt, &auml;ndern wir den als Parameter &uuml;bergebenen Modulnamen, um einen weiteren Parameter zu sparen, der diesen Zustand an die aufrufende Routine weitergibt.<\/p>\n<p>Nun liegt also eine Kopie des Moduls im Textformat vor &#8211; und das, obwohl eigentlich noch gar nicht feststeht, ob das Modul &uuml;berhaupt in einer neuen Version gespeichert werden soll.<\/p>\n<p>Warum schon vorher so viel Arbeit investieren Ganz einfach: Es gibt keine andere M&ouml;glichkeit, den Inhalt eines Moduls mit dem in der Tabelle <b>tblVersionen <\/b>gespeicherten Inhalt zu vergleichen, als &uuml;ber den per <b>SaveAsText <\/b>gespeicherten Inhalt.<\/p>\n<p>Die Vorbereitung f&uuml;r diesen Vergleich geschieht wieder in der Routine <b>ModuleSpeichern<\/b>.<\/p>\n<p>Sie ruft die Funktion <b>LoadModule <\/b>auf, die auf die soeben gespeicherte Datei zugreift und den Inhalt in einer Textvariablen speichert (6).<\/p>\n<p>Die Routine sieht wie folgt aus und liefert den Inhalt der Textdatei als String zur&uuml;ck:<\/p>\n<pre>Public Function LoadModule() As String\r\n    Dim strFilename As String\r\n    strFilename = _\r\n    CurrentProject.Path &amp; &quot;\\temp.txt&quot;\r\n    Open strFilename For Input As #1\r\n    LoadModule = Input$(LOF(1), #1)\r\n    Close #1\r\nEnd Function<\/pre>\n<p>Dabei ist zu ber&uuml;cksichtigen, dass s&auml;mtliche tempor&auml;r auf der Festplatte gespeicherten Module dort unter dem Namen <b>temp.txt <\/b>im Verzeichnis der aktuellen Datenbank liegen. Wurde dieses Modul &uuml;berhaupt schon einmal gespeichert<\/p>\n<p>Diese Frage beantwortet die Funktion <b>MustSaveObject<\/b>, die in der Tabelle <b>tblModule <\/b>nach einem Modul mit dem Namen des aktuell bearbeiteten Objekts sucht (7):<\/p>\n<pre>Public Function MustSaveObject(strObjectname _\r\n    As String) As Boolean\r\n    MustSaveObject = Nz(DLookup(&quot;ModulID&quot;, _\r\n    &quot;tblModule&quot;, &quot;Modulname LIKE &apos;&quot; _\r\n    &amp; strObjectname &amp; &quot;&apos;&quot;), 0) = 0\r\nEnd Function<\/pre>\n<p>Ist das nicht der Fall, legt die Routine <b>SaveObject <\/b>mit einer <b>INSERT INTO<\/b>-SQL-Abfrage einen entsprechenden Datensatz in der Tabelle <b>tblModule <\/b>an (9):<\/p>\n<pre>Public Sub SaveObject(strModulname As String, _\r\nlngModultyp As AcModuleType)\r\nDim db As DAO.Database\r\nSet db = CurrentDb\r\ndb.Execute &quot;INSERT INTO tblModule SELECT &apos;&quot; _\r\n&amp; strModulname &amp; &quot;&apos; AS Modulname, &quot; _\r\n&amp; lngModultyp &amp; &quot; AS Modultyp&quot;, _\r\ndbFailOnError\r\nEnd Sub<\/pre>\n<p>Und dann kommt endlich die in der Variablen <b>strModulinhalt <\/b>gespeicherte Kopie des aktuellen Inhalts des Objekts zum Tragen:<\/p>\n<p>Die Prozedur <b>MustSaveVersion <\/b>(s. Listing 4) vergleicht den Inhalt der Variablen, also den aktuellen Zustand, mit dem zuletzt in der Tabelle <b>tblVersionen <\/b>gespeicherten (8). <\/p>\n<p class=\"kastentabelleheader\">Listing 4: Vergleichen der aktuellen Version mit der zuletzt gespeicherten Fassung<\/p>\n<pre>Public Function MustSaveVersion(lngModulID As Long, strModulinhalt As String) As Boolean\r\n    Dim db As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Set db = CurrentDb\r\n    Set rst = db.OpenRecordset(&quot;SELECT TOP 1 Modulinhalt FROM tblVersionen WHERE ModulID =&quot; _\r\n    &amp; lngModulID &amp; &quot; ORDER BY Speicherdatum DESC&quot;, dbOpenDynaset)\r\n    If Not rst.EOF Then\r\n        If Not StrComp(strModulinhalt, rst!Modulinhalt, vbTextCompare) = 0 Then\r\n            MustSaveVersion = True\r\n        End If\r\n    Else\r\n        MustSaveVersion = True\r\n    End If\r\nEnd Function<\/pre>\n<p>Unterscheiden sich die beiden Versionen, schreibt die Prozedur <b>MakeNewVersion <\/b>(s. Listing 5) einen neuen Datensatz mit dem aktuellen Inhalt des Moduls in die Tabelle <b>tblVersionen <\/b>(10).<\/p>\n<p class=\"kastentabelleheader\">Listing 5: Erstellen einer neuen Version in der Tabelle tblVersionen<\/p>\n<pre>Public Sub MakeNewVersion(lngModulID As Long, strModulinhalt As String)\r\nDim db As DAO.Database\r\nDim rst As DAO.Recordset\r\nSet db = CurrentDb\r\nSet rst = db.OpenRecordset(&quot;SELECT * FROM tblVersionen WHERE 1 = 2&quot;, dbOpenDynaset)\r\nrst.AddNew\r\nrst!ModulID = lngModulID\r\nrst!Modulinhalt.AppendChunk strModulinhalt\r\nrst!Speicherdatum = Now\r\nrst.Update\r\nrst.Close\r\nSet rst = Nothing\r\nSet db = Nothing\r\nEnd Sub<\/pre>\n<p>Schlie&szlig;lich werden noch die tempor&auml;ren Objekte gel&ouml;scht, was die Routine <b>TemporaereObjekteLoeschen <\/b>aus Listing 6 erledigt (11).<\/p>\n<p class=\"kastentabelleheader\">Listing 6: Tempor&auml;re Objekte und Dateien l&ouml;schen<\/p>\n<pre>Public Sub TemporaereObjekteLoeschen()\r\nDim db As DAO.Database\r\nDim rst As DAO.Recordset\r\nDim lngObjecttype As AcModuleType\r\nSet db = CurrentDb\r\nSet rst = db.OpenRecordset(&quot;SELECT * FROM MSysObjects &quot; _\r\n&amp; &quot;WHERE Name LIKE &apos;*_----&apos;&quot;, dbOpenDynaset)\r\nDo While Not rst.EOF\r\n    Select Case rst!Type\r\n    Case -32761 &apos;Modul\r\n    lngObjecttype = acModule\r\n    Case -32764 &apos;Report\r\n    lngObjecttype = acReport\r\n    Case -32768 &apos;Form\r\n    lngObjecttype = acForm\r\n    Case 5\r\n    lngObjecttype = acQuery\r\n    End Select\r\n    DoCmd.DeleteObject lngObjecttype, rst!Name\r\n    rst.MoveNext\r\nLoop\r\nKill CurrentProject.Path &amp; &quot;\\temp.txt&quot;\r\nEnd Sub<\/pre>\n<p>Diese k&uuml;mmert sich zun&auml;chst um die in (5) angelegten tempor&auml;ren Objekte in der Datenbank, deren Objektname ja aus dem Originalnamen plus einer angeh&auml;ngten GUID besteht.<\/p>\n<p>Auch diese Objekte sind in der Tabelle <b>MSysObjects <\/b>aufgef&uuml;hrt. Dementsprechend ermittelt die Routine zun&auml;chst alle Objekte, deren Name mit einer GUID endet.<\/p>\n<p>Dies l&auml;sst sich relativ einfach durch den Vergleichsausdruck <b>Name LIKE &apos;*_&#8212;-&apos;<\/b> erledigen. Das Sternchen entspricht dabei dem urspr&uuml;nglichen Objektnamen und der Rest ist ein Platzhalter f&uuml;r eine beliebige, aber immer gleich aufgebaute GUID.<\/p>\n<h2>Benutzeroberfl&auml;che<\/h2>\n<p>Die Hauptfunktion <b>ModuleSpeichern<\/b>, die Sie soeben kennen gelernt haben, wird von verschiedenen Stellen des Formulars <b>frmAccVersion <\/b>aus mit verschiedenen Parametern aufgerufen.<\/p>\n<p>Beim Schlie&szlig;en des Formulars erfolgt der Aufruf mit dem Wert <b>True <\/b>f&uuml;r den Parameter <b>bolZwischenspeichern<\/b>. Dieser legt fest, ob auch der aktuelle Stand nicht gespeicherter Abfragen, Formulare und Berichte gesichert werden soll. Da dies einige Rechenzeit f&uuml;r das Kopieren, Speichern, Vergleichen und Versionieren erfordert, soll dies nur beim Schlie&szlig;en der Anwendung geschehen.<\/p>\n<h2>Steuerelemente<\/h2>\n<p>Das Listenfeld <b>lstModule <\/b>zeigt alle in der Tabelle <b>tblModule<\/b> gespeicherten Module an. Die Datensatzherkunft wird im Ereignis <b>Beim Anzeigen <\/b>des Formulars gef&uuml;llt:<\/p>\n<pre>Private Sub Form_Current()\r\nMe!lstModule.RowSource = _\r\n&quot;SELECT tblModule.ModulID, tblModule.&quot; _\r\n&amp; &quot;Modulname FROM tblModule &quot; _\r\n&amp; &quot;ORDER BY tblModule.Modulname;&quot;\r\nMe!lstModule = Me!lstModule.ItemData(0)\r\nVersionenAktualisieren\r\nEnd Sub<\/pre>\n<p>Diese Routine ruft gleichzeitig die Prozedur <b>VersionenAktualisieren <\/b>auf. Diese f&uuml;llt das Listenfeld <b>lstVersionen <\/b>mit den Datens&auml;tzen der Tabelle <b>tblVersionen<\/b>, die zu dem Modul geh&ouml;ren, das im Listenfeld <b>lstModule <\/b>angezeigt wird:<\/p>\n<pre>Private Sub VersionenAktualisieren()\r\nMe!lstVersionen.RowSource = _\r\n&quot;SELECT VersionID, Speicherdatum &quot; _\r\n&amp; &quot;FROM tblVersionen WHERE ModulID = &quot; _\r\n&amp; Me.lstModule _\r\n&amp; &quot; ORDER BY Speicherdatum DESC&quot;\r\nMe!lstVersionen = Me!lstVersionen.ItemData(0)\r\nEnd Sub<\/pre>\n<p>Die folgende Ereignisprozedur sorgt daf&uuml;r, dass dies auch beim Aktualisieren des Listenfelds <b>lstModule <\/b>geschieht:<\/p>\n<pre>Private Sub lstModule_AfterUpdate()\r\nVersionenAktualisieren\r\nEnd Sub<\/pre>\n<p class=\"zwischen-berschrift-oberer-spaltenrand\">Wann speichern<\/p>\n<p>Es gibt drei M&ouml;glichkeiten f&uuml;r den Aufruf der oben beschriebenen Routine <b>ModuleSpeichern<\/b>. Der erste erfolgt ganz einfach &uuml;ber einen Mausklick auf die Schaltfl&auml;che <b>cmdStandSpeichern<\/b>:<\/p>\n<pre>Private Sub cmdStandSpeichern_Click()\r\nModuleSpeichern True\r\nMe!lstModule.Requery\r\nVersionenAktualisieren\r\nEnd Sub<\/pre>\n<p>Nach dem Speichern sorgt die Routine f&uuml;r das Aktualisieren der Listenfelder. Die zweite M&ouml;glichkeit ist zeitgesteuert. Mit den Steuerelementen im unteren Bereich k&ouml;nnen Sie festlegen, ob dies &uuml;berhaupt geschehen soll und in welchem Intervall. Ein Klick auf das Kontrollk&auml;stchen <b>ctlZwischenspeichern <\/b>l&ouml;st die folgende Routine aus, die zun&auml;chst diese Einstellung in der Registry speichert, damit diese beim n&auml;chsten Aufruf automatisch gelesen werden kann.<\/p>\n<p>Au&szlig;erdem aktiviert oder deaktiviert sie das Textfeld zur Angabe des Speicherintervalls.<\/p>\n<pre>Private Sub ctlZwischenspeichern_AfterUpdate()\r\nSaveSetting &quot;AccVersion&quot;, &quot;Konfiguration&quot;, _\r\n&quot;Zwischenspeichern&quot;, _\r\nMe!ctlZwischenspeichern\r\nIf Me!ctlZwischenspeichern = True Then\r\n    Me!txtSpeicherintervall.Enabled = True\r\nElse\r\n    Me!txtSpeicherintervall.Enabled = False\r\nEnd If\r\nEnd Sub<\/pre>\n<p>Eine &auml;nderung des Intervalls ist nur bei aktiviertem Textfeld <b>txtSpeicherintervall <\/b>m&ouml;glich und l&ouml;st die folgende Prozedur aus. Der eingegebene Wert wird zun&auml;chst ebenfalls in die Registry geschrieben.<\/p>\n<p>Falls das Zwischenspeichern aktiviert ist, stellt die Routine die Eigenschaft <b>TimerIntervall <\/b>des Formulars <b>frmAccVersion<\/b> auf den entsprechenden Wert ein.<\/p>\n<p>Dies sorgt daf&uuml;r, dass die f&uuml;r das Ereignis <b>Bei Zeitgeber <\/b>festgelegte Prozedur in entsprechenden Abst&auml;nden aufgerufen wird.<\/p>\n<pre>Private Sub txtSpeicherintervall_AfterUpdate()\r\nSaveSetting &quot;AccVersion&quot;, &quot;Konfiguration&quot;, _\r\n&quot;Speicherintervall&quot;, _\r\nMe!txtSpeicherintervall\r\nIf Me!ctlZwischenspeichern = True Then\r\n    Me.TimerInterval = _\r\n    Me!txtSpeicherintervall * 60 * 1000\r\n    Me!txtSpeicherintervall.Enabled = True\r\nElse\r\n    Me.TimerInterval = 0\r\n    Me!txtSpeicherintervall.Enabled = False\r\nEnd If\r\nEnd Sub<\/pre>\n<p>Die durch den Zeitgeber ausgel&ouml;ste Prozedur enth&auml;lt lediglich eine Anweisung:<\/p>\n<pre>Private Sub Form_Timer()\r\nModuleSpeichern True\r\nEnd Sub<\/pre>\n<p>Die dritte M&ouml;glichkeit zum Aufrufen von <b>ModuleSpeichern <\/b>befindet sich im Ereignis <b>Beim Entladen <\/b>des Formulars. Es wird automatisch beim Schlie&szlig;en des Formulars ausgel&ouml;st. Damit der Benutzer das Formular nicht willk&uuml;rlich schlie&szlig;en kann, wurden die Schlie&szlig;en-Schaltfl&auml;che und andere Elemente der Titelleiste entfernt. Es gibt nur noch eine Schaltfl&auml;che im Formular zum Unsichtbarmachen.<\/p>\n<pre>Private Sub Form_Unload(Cancel As Integer)\r\nCancel = Not ModuleSpeichern(False)\r\nEnd Sub<\/pre>\n<p>Tritt in <b>ModuleSpeichern<\/b> ein Fehler auf, liefert dieses den Wert <b>False <\/b>zur&uuml;ck. Die Anwendung wird dann aus Sicherheitsgr&uuml;nden nicht geschlossen.<\/p>\n<h2>Version wiederherstellen<\/h2>\n<p>Mit der Schaltfl&auml;che <b>cmdUeberschreiben<\/b> kann man eine Version wiederherstellen, indem man sie &uuml;ber die bestehende Fassung des Objekts schreibt.<\/p>\n<p>Die dadurch ausgel&ouml;ste Routine sieht wie in Listing 7 aus. Sie fragt zun&auml;chst nach, ob man die bestehende Fassung wirklich &uuml;berschreiben m&ouml;chte, und bricht bei negativer Antwort ab.<\/p>\n<p class=\"kastentabelleheader\">Listing 7: Ein Objekt durch &Uuml;berschreiben eines bestehenden Objekts wiederherstellen<\/p>\n<pre>Private Sub cmdUeberschreiben_Click()\r\n    Dim db As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    If MsgBox(&quot;M&ouml;chten Sie die bestehende Fassung wirklich &uuml;berschreiben&quot;, vbYesNo, _\r\n    &quot;&Uuml;berschreiben&quot;) = vbYes Then\r\n    Set db = CurrentDb\r\n    Set rst = db.OpenRecordset(&quot;SELECT * FROM qryModuleVersionen WHERE VersionID = &quot; _\r\n    &amp; Me!lstVersionen.ItemData(Me!lstVersionen.ItemsSelected(0)), dbOpenDynaset)\r\n    Open CurrentProject.Path &amp; &quot;\\temp.txt&quot; For Output As #1\r\n    Print #1, rst!Modulinhalt\r\n    Close #1\r\n    DoCmd.Rename rst!Modulname &amp; &quot;_tmp&quot;, rst!Modultyp, rst!Modulname\r\n    LoadFromText rst!Modultyp, rst!Modulname, CurrentProject.Path &amp; &quot;\\temp.txt&quot;\r\n    DoCmd.DeleteObject rst!Modultyp, rst!Modulname &amp; &quot;_tmp&quot;\r\n    Application.RefreshDatabaseWindow\r\nEnd If\r\nEnd Sub<\/pre>\n<p>Anderenfalls &ouml;ffnet sie eine Datensatzgruppe auf Basis der Abfrage <b>qryModuleVersionen<\/b>, die alle Felder der beiden Tabellen <b>tblModule <\/b>und <b>tblVersionen <\/b>enth&auml;lt und nach der ID des im Listenfeld <b>lstVersion <\/b>ausgew&auml;hlten Eintrags gefiltert ist.<\/p>\n<p>Dann &ouml;ffnet die Routine eine Textdatei und schreibt den Modultext hinein.<\/p>\n<p>Sie benennt das bestehende Objekt um und legt dann mit der Methode <b>LoadFromText <\/b>ein neues, auf der Textdatei bestehendes Objekt an. Anschlie&szlig;end l&ouml;scht sie das tempor&auml;r erstelle Objekt und aktualisiert das Datenbankfenster.<\/p>\n<h2>Objekte testweise wiederherstellen<\/h2>\n<p>Wenn ein Objekt beispielsweise versioniert, aber nicht gespeichert wurde, kann der Benutzer dieses Objekt auch wiederherstellen, ohne das bestehende Objekt zu &uuml;berschreiben, und es zun&auml;chst pr&uuml;fen. Dies erledigt die Prozedur aus Listing 8.<\/p>\n<p class=\"kastentabelleheader\">Listing 8: Ein Objekt unter einem anderen Namen wiederherstellen<\/p>\n<pre>Private Sub cmdWiederherstellenAls_Click()\r\n    Dim db As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim var As Variant\r\n    Set db = CurrentDb\r\n    For Each var In Me.lstVersionen.ItemsSelected\r\n        Set rst = db.OpenRecordset(&quot;SELECT * FROM qryModuleVersionen WHERE VersionID = &quot; _\r\n        &amp; Me!lstVersionen.ItemData(var), dbOpenDynaset)\r\n        Open CurrentProject.Path &amp; &quot;\\temp.txt&quot; For Output As #1\r\n        Print #1, rst!Modulinhalt\r\n        Close #1\r\n        On Error Resume Next\r\n        LoadFromText rst!Modultyp, &quot;__&quot; &amp; rst!Modulname _\r\n        &amp; Format(rst!Speicherdatum, &quot;_yyyymmdd_hhnnss&quot;), CurrentProject.Path &amp; &quot;\\temp.txt&quot;\r\n        If Err.Number = 2950 Then\r\n            MsgBox &quot;Dieses Objekt ist bereits vorhanden. &quot; _\r\n            &amp; &quot;L&ouml;schen Sie es zun&auml;chst oder benennen Sie es um.&quot;\r\n        End If\r\n        On Error GoTo 0\r\n    Next var\r\n    Application.RefreshDatabaseWindow\r\n    End Sub<\/pre>\n<p>Der Einfachheit halber soll dies auch f&uuml;r mehrere Versionen gleichzeitig m&ouml;glich sein. Deshalb gibt es eine Schleife, die alle markierten Eintr&auml;ge des Listenfeldes durchl&auml;uft. Damit der Benutzer im Listenfeld mehrere Eintr&auml;ge gleichzeitig markieren kann, stellen Sie die Eigenschaft <b>Mehrfachauswahl <\/b>auf einen anderen Wert als <b>Keine <\/b>ein, in diesem Fall auf <b>Erweitert<\/b>.<\/p>\n<p>Innerhalb der Schleife &ouml;ffnet die Routine jeweils ein Recordset mit einem Datensatz der Abfrage <b>qryModuleVersionen<\/b>. Der Modultext wird auch hier in einer Textdatei gespeichert, damit er anschlie&szlig;end mit der Methode <b>LoadFromText <\/b>als Objekt angelegt werden kann.<\/p>\n<p>Der Objektname besteht diesmal aus dem urspr&uuml;nglichen Objektnamen und einer aus einem Unterstrich und dem Datum und der Uhrzeit der Speicherung bestehenden Erweiterung.<\/p>\n<p>M&ouml;glicherweise ist schon ein Objekt gleichen Namens vorhanden; in diesem Fall wird der Vorgang mit einem entsprechenden Hinweis abgebrochen.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Die hier vorgestellte L&ouml;sung bietet eine M&ouml;glichkeit, den Fortschritt beim Entwickeln der Objekte und Module einer Datenbank in passenden Intervallen zu sichern. Zus&auml;tzlich bietet sie eine gewissen Sicherheit und das auf zweierlei Weise:<\/p>\n<ul>\n<li class=\"aufz-hlung\">Erstens k&ouml;nnen Sie das Tool so einstellen, dass es in regelm&auml;&szlig;igen Intervallen alle &auml;nderungen am Code und am Entwurf von Abfragen, Formularen und Berichten speichert. Sollte die Datenbank oder das System einmal abst&uuml;rzen, k&ouml;nnen Sie wie in Word auf Zwischenspeicherungen zur&uuml;ckgreifen.<\/li>\n<li class=\"aufz-hlung\">Zweitens k&ouml;nnen Sie beim Schlie&szlig;en der Datenbank nun ruhig einmal vergessen, den letzten Stand der Objekte zu speichern &#8211; <b>AccVersion <\/b>&uuml;bernimmt dies f&uuml;r Sie. Gleichzeitig weist es Sie beim n&auml;chsten &Ouml;ffnen darauf hin, wenn nicht gespeicherte &auml;nderungen versioniert wurden.<\/li>\n<\/ul>\n<p>Es gibt allerdings noch eine Menge Erweiterungsm&ouml;glichkeiten f&uuml;r dieses Tool.<\/p>\n<p>So muss man derzeit noch die f&uuml;nf ben&ouml;tigten Elemente in die Datenbank importieren, in der man die Versionierung einsetzen m&ouml;chte.<\/p>\n<p>Praktischer w&auml;re es sicherlich, wenn <b>AccVersion <\/b>als COM-Add-In daherk&auml;me und man dort festlegen k&ouml;nnte, in welchen Datenbanken das Tool t&auml;tig werden soll.<\/p>\n<p>Die bisher in den beiden Tabellen <b>tblModule <\/b>und <b>tblVersionen <\/b>gespeicherten Daten k&ouml;nnte man auch extern speichern. F&uuml;rs Erste scheint das Sichern der Versionen in Tabellen, die sich in der selben Datenbank wie die zu sichernden Daten befinden, ausreichend &#8211; zumindest solange man diese Datenbank hin und wieder sichert.<\/p>\n<p>Aber das sollten Sie ohnehin tun, denn der Stand des Datenmodells wird von <b>AccVersion <\/b>nicht ber&uuml;cksichtigt.<\/p>\n<p>Und das ist auch ein Punkt: Es w&auml;re doch toll, wenn man verschiedene St&auml;nde der Entwicklung des Datenmodells ebenfalls sichern k&ouml;nnte.<\/p>\n<p>Hier ist allerdings noch eine Menge mehr Arbeit erforderlich, denn Tabellen lassen sich nicht so einfach per <b>SaveAsText <\/b>auf der Festplatte speichern &#8211; auch wenn der Befehl eine passende Konstante hergibt, die sich aber als nicht funktionell erweist.<\/p>\n<p>Schlie&szlig;lich fehlt dem Tool noch jegliche Fehlerbehandlung &#8211; f&uuml;r den Produktiveinsatz sollten Sie es deshalb nur mit Bedacht einsetzen.<\/p>\n<p>Sollte Ihnen die grunds&auml;tzliche Idee dieser Access-internen Versionierung gefallen, schreiben Sie es uns &#8211; am besten per E-Mail an <b>info@access-im-unternehmen.de<\/b>.<\/p>\n<p>Bei entsprechendem Interesse verfolgen wir dieses Thema weiter und liefern in den n&auml;chsten Monaten zus&auml;tzliche Features nach. Dazu k&ouml;nnen Sie uns nat&uuml;rlich auch gern eigene Ideen liefern &#8211; wir sind schon gespannt auf Ihr Feedback!<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<p class=\"kastentabelleheader\">Im Soforteinsatz<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Wenn Sie die Versionierung in der aktuellen Fassung in einer eigenen Anwendung einsetzen m&ouml;chten, importieren Sie dort die Objekte <b>frmAccVersion<\/b>, <b>frmProgress<\/b>, <b>qryModuleVersionen<\/b>, <b>mdlAccVersion <\/b>und <b>mdlFortschrittsanzeige <\/b>in die Zieldatenbank. Sorgen Sie daf&uuml;r, dass das Formular <b>frmAccVersion <\/b>beim Start der Datenbank ge&ouml;ffnet wird &#8211; beispielsweise, indem Sie es als Startformular der Anwendung eintragen.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>accVersion.mdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/D524B76D-70DD-4295-8B6E-19F80FF92D01\/aiu_610.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>So, jetzt habe ich endg&uuml;ltig die Nase voll. Schon wieder ist es passiert: Mit letzter Kraft eine wichtige Routine zu Ende programmiert und jetzt nur noch Access schlie&szlig;en, den Rechner runterfahren und ab ins Bett. Und am n&auml;chsten Morgen die Ern&uuml;chterung: Die &Auml;nderungen sind nicht mehr da! Da habe ich wohl mal wieder mit &#8222;Nein&#8220; auf die Frage geantwortet, ob ich die ge&auml;nderten Objekte speichern m&ouml;chte &#8230; Aber damit ist jetzt Schluss: Ich baue mir ein Tool, das regelm&auml;&szlig;ig meine Formulare, Berichte und Module speichert &#8211; und dabei auch noch alte Versionen aufbewahrt.<\/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":[662008,66032008,44000028,44000027],"tags":[],"class_list":["post-55000610","post","type-post","status-publish","format-standard","hentry","category-662008","category-66032008","category-Ergonomie_und_Benutzeroberflaeche","category-Loesungen"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Quellcode-Versionsverwaltung inside - 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\/QuellcodeVersionsverwaltung_inside\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Quellcode-Versionsverwaltung inside\" \/>\n<meta property=\"og:description\" content=\"So, jetzt habe ich endg&uuml;ltig die Nase voll. Schon wieder ist es passiert: Mit letzter Kraft eine wichtige Routine zu Ende programmiert und jetzt nur noch Access schlie&szlig;en, den Rechner runterfahren und ab ins Bett. Und am n&auml;chsten Morgen die Ern&uuml;chterung: Die &Auml;nderungen sind nicht mehr da! Da habe ich wohl mal wieder mit &quot;Nein&quot; auf die Frage geantwortet, ob ich die ge&auml;nderten Objekte speichern m&ouml;chte ... Aber damit ist jetzt Schluss: Ich baue mir ein Tool, das regelm&auml;&szlig;ig meine Formulare, Berichte und Module speichert - und dabei auch noch alte Versionen aufbewahrt.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-11T11:50:33+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg09.met.vgwort.de\/na\/42efa9541fb745e9b2f3fefd1118e83b\" \/>\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=\"27\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Quellcode-Versionsverwaltung inside\",\"datePublished\":\"2020-05-11T11:50:33+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/\"},\"wordCount\":4313,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/42efa9541fb745e9b2f3fefd1118e83b\",\"articleSection\":[\"2008\",\"3\\\/2008\",\"Ergonomie und Benutzeroberfl\u00e4che\",\"L\u00f6sungen\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/\",\"name\":\"Quellcode-Versionsverwaltung inside - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/42efa9541fb745e9b2f3fefd1118e83b\",\"datePublished\":\"2020-05-11T11:50:33+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/42efa9541fb745e9b2f3fefd1118e83b\",\"contentUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/42efa9541fb745e9b2f3fefd1118e83b\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/QuellcodeVersionsverwaltung_inside\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Quellcode-Versionsverwaltung inside\"}]},{\"@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":"Quellcode-Versionsverwaltung inside - 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\/QuellcodeVersionsverwaltung_inside\/","og_locale":"de_DE","og_type":"article","og_title":"Quellcode-Versionsverwaltung inside","og_description":"So, jetzt habe ich endg&uuml;ltig die Nase voll. Schon wieder ist es passiert: Mit letzter Kraft eine wichtige Routine zu Ende programmiert und jetzt nur noch Access schlie&szlig;en, den Rechner runterfahren und ab ins Bett. Und am n&auml;chsten Morgen die Ern&uuml;chterung: Die &Auml;nderungen sind nicht mehr da! Da habe ich wohl mal wieder mit \"Nein\" auf die Frage geantwortet, ob ich die ge&auml;nderten Objekte speichern m&ouml;chte ... Aber damit ist jetzt Schluss: Ich baue mir ein Tool, das regelm&auml;&szlig;ig meine Formulare, Berichte und Module speichert - und dabei auch noch alte Versionen aufbewahrt.","og_url":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-11T11:50:33+00:00","og_image":[{"url":"http:\/\/vg09.met.vgwort.de\/na\/42efa9541fb745e9b2f3fefd1118e83b","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"27\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Quellcode-Versionsverwaltung inside","datePublished":"2020-05-11T11:50:33+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/"},"wordCount":4313,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/42efa9541fb745e9b2f3fefd1118e83b","articleSection":["2008","3\/2008","Ergonomie und Benutzeroberfl\u00e4che","L\u00f6sungen"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/","url":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/","name":"Quellcode-Versionsverwaltung inside - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/42efa9541fb745e9b2f3fefd1118e83b","datePublished":"2020-05-11T11:50:33+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/#primaryimage","url":"http:\/\/vg09.met.vgwort.de\/na\/42efa9541fb745e9b2f3fefd1118e83b","contentUrl":"http:\/\/vg09.met.vgwort.de\/na\/42efa9541fb745e9b2f3fefd1118e83b"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/QuellcodeVersionsverwaltung_inside\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Quellcode-Versionsverwaltung inside"}]},{"@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\/55000610","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=55000610"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55000610\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55000610"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55000610"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55000610"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}