{"id":55001513,"date":"2024-10-01T00:00:00","date_gmt":"2024-08-27T08:47:18","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1513"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Fehler_in_der_Runtime_von_Access_finden","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/","title":{"rendered":"Fehler in der Runtime von Access finden"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg02.met.vgwort.de\/na\/1d542d5e076047b68450622e9d9da024\" width=\"1\" height=\"1\" alt=\"\"><b>Wenn wir eine Datenbank, die unter der Vollversion von Access fehlerfrei l&auml;uft, in der Runtime &ouml;ffnen, kann es zu unerkl&auml;rlichen Fehlern kommen. Die Runtime verabschiedet sich dann in der Regel mit einer Meldung wie &#8222;Die Ausf&uuml;hrung dieser Anwendung wurde wegen eines Laufzeitfehlers angehalten.&#8220; Damit k&ouml;nnen wir nat&uuml;rlich erst einmal nicht viel anfangen. Anlass zu diesem Beitrag ist ein konkretes Problem einer Kundin, deren Anwendung auf der Runtime-Version von Access nicht wie gew&uuml;nscht funktioniert. Beim Testen der Anwendung in der Runtime kam ich jedoch gar nicht erst soweit wie die Kundin. Es tauchte bereits vorher die besagte Meldung auf. Wie k&ouml;nnen wir nun herausfinden, was genau den Fehler verursacht und welche Zeile ihn ausl&ouml;st? Vorgehensweisen dazu stellen wir in diesem Beitrag vor.<\/b><\/p>\n<h2>Optimale L&ouml;sung: Die Anwendung ist vollst&auml;ndig mit einer Fehlerbehandlung ausgestattet.<\/h2>\n<p>Probleme wie das geschilderte sollten eigentlich gar nicht in dieser Form auftreten. &#8222;In dieser Form&#8220; bedeutet, dass es durchaus einmal vorkommen kann, dass eine Anwendung unter der Runtime-Version von Access anders arbeitet als unter der Vollversion.<\/p>\n<p>Aber wenn dabei Probleme auftauchen, werden diese in der Regel durch Fehler im VBA-Code ausgel&ouml;st. Und wenn die Meldung aus Bild 1 erscheint, ist die Wahrscheinlichkeit hoch, dass es in der Runtime einen Fehler gibt, der durch eine VBA-Anweisung ausgel&ouml;st wurde.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_05\/pic_1513_001.png\" alt=\"Typische Fehlermeldung der Runtime\" width=\"599,559\" height=\"159,7324\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Typische Fehlermeldung der Runtime<\/span><\/b><\/p>\n<p>Eine professionell programmierte Anwendung w&uuml;rde in jeder Prozedur eine Fehlerbehandlung enthalten, die beim Auftreten eines Fehlers daf&uuml;r sorgt, dass diese nicht die eingebaute Access-Fehlermeldung ausl&ouml;st. Genau diese wird n&auml;mlich durch die hier gezeigte Fehlermeldung der Runtime-Version kaschiert.<\/p>\n<h2>Fehler im Griff mit umfassender Fehlerbehandlung<\/h2>\n<p>Wenn wir fl&auml;chendeckend unsere eigene Fehlerbehandlung in den Routinen der Anwendung untergebracht h&auml;tten, w&auml;re die eingebaute Fehlermeldung der Runtime gar nicht n&ouml;tig. Eine solche Fehlerbehandlung ist relativ einfach zu realisieren, wenn man dies gleich von Beginn an ber&uuml;cksichtigt. Wenn man jedoch erst einmal eine umfangreiche Anwendung programmiert hat und anschlie&szlig;end die Fehlerbehandlung hinzuf&uuml;gen will, kommt eine Menge Arbeit auf einen zu.<\/p>\n<h2>Fehlerbehandlung per MZ-Tools<\/h2>\n<p>Auch diese kann man weitgehend vereinfachen, indem man ein Werkzeug wie <b>MZ-Tools <\/b>zum halbautomatischen Anlegen der Fehlerbehandlung nutzt. Mit MZ-Tools k&ouml;nnen wir sogar automatisch Zeilennummern hinzuf&uuml;gen, was in einem Runtime-Setting essenziell ist, denn sonst k&ouml;nnen wir kaum herausfinden, wo in einer l&auml;ngeren Prozedur genau der Fehler aufgetreten ist.<\/p>\n<h2>Fehlerbehandlung per vbWatchDog<\/h2>\n<p>Die n&auml;chste M&ouml;glichkeit ist der <b>vbWatchDog<\/b>, ein Add-In von Access-Spezialist Wayne Philips. Das ist ein Tool, mit dem man nur durch Hinzuf&uuml;gen einiger weniger Objekte und ein wenig Code die komplette Anwendung mit einer allgemeinen Fehlerbehandlung ausstattet, ohne den einzelnen Prozeduren auch nur eine einzige Zeile Code hinzuzuf&uuml;gen (es sei denn, es ist eine spezielle Fehlerbehandlung erw&uuml;nscht).<\/p>\n<h2>Problem bei der Runtime: Kein Debug.Print m&ouml;glich<\/h2>\n<p>Wenn wir eine Anwendung in der Vollversion testen, um ein eventuell fehlerhaftes Verhalten zu pr&uuml;fen, hilft uns oft die <b>Debug.Print<\/b>-Anweisung weiter. Damit k&ouml;nnen wir praktisch vor oder nach jeder Zeile Informationen im Direktbereich des VBA-Editors ausgeben.<\/p>\n<p>Leider hilft uns auch das in einer unter der Runtime-Version von Access ausgef&uuml;hrten Anwendung nicht weiter, denn hier gibt es gar keinen VBA-Editor, mit dem wir uns die Ausgabe ansehen k&ouml;nnten.<\/p>\n<h2>Herausfinden, wo ein Fehler stattfindet, ohne alles mit Fehlerbehandlungen auszustatten<\/h2>\n<p>Typischerweise funktionieren Dinge, die unter einer Access-Vollversion reibungslos laufen, gelegentlich in der Runtime-Version nicht. Wenn wir keine Fehlerbehandlung eingebaut haben und vielleicht auch gerade nicht die Ressourcen verf&uuml;gbar sind, um das nachzuholen, m&uuml;ssen wir ein wenig kleinschrittiger an das Problem herangehen.<\/p>\n<p>Wir nehmen nun zus&auml;tzlich noch an, dass wir es mit einer Anwendung eines Kunden zu tun haben, die wir nicht so genau kennen wie unsere selbst programmierten Anwendungen.<\/p>\n<p>Dann sehen wir vielleicht vor dem Erscheinen der omin&ouml;sen Fehlermeldung noch, welche Formulare angezeigt werden, aber welche Prozedur den Fehler ausgel&ouml;st hat, finden wir auf diese Weise auch nicht heraus.<\/p>\n<p>Wenn wir nicht alle Prozeduren mit einer Fehlerbehandlung versehen wollen, m&uuml;ssen wir einen anderen Weg gehen, um herauszufinden, welche Stelle im Code den Fehler ausl&ouml;st. Dazu gehen wir wie folgt vor:<\/p>\n<ul>\n<li>Wir f&uuml;gen jeder Prozedur eine Anweisung hinzu, die unmittelbar hinter der Kopfzeile der Prozedur angelegt wird. Diese soll eine Hilfsfunktion aufrufen, die den Namen des Moduls, den Namen der Prozedur und Datum und Uhrzeit des Fehlers notiert. Die Hilfsfunktion soll diese Daten in eine eigens daf&uuml;r angelegte Tabelle schreiben.<\/li>\n<li>Dann kopieren wir die Datenbank auf den Rechner mit der Runtime-Version von Access und f&uuml;hren diese dort aus. Wir sollten nun den Fehler wie gewohnt erhalten.<\/li>\n<li>Der Unterschied ist nun jedoch, dass wir in der Tabelle zum Aufzeichnen der aufgerufenen Prozeduren einige Eintr&auml;ge vorfinden, von denen der letzte vermutlich die Prozedur enth&auml;lt, die den Fehler ausgel&ouml;st hat. Um diese zu lesen, kopieren wir die Datenbank wieder auf den Rechner mit der Vollversion von Access zur&uuml;ck.<\/li>\n<li>F&uuml;r die gefundene Prozedur f&uuml;gen wir nun eine Fehlerbehandlung hinzu sowie eine Zeilennummerierung. Auch die hierbei anfallenden Informationen schreiben wir wieder in eine Tabelle.<\/li>\n<li>Wenn wir die Datenbank nun wieder auf den Runtime-Rechner verschieben und ausf&uuml;hren, wird Access den Fehler nicht erneut anzeigen, da dieser ja nun behandelt wird. Es ist jedoch wahrscheinlich, dass nun ein Folgefehler dazu f&uuml;hrt, dass die von der Runtime gelieferte Fehlermeldung erscheint. Das ist jedoch erst einmal irrelevant &#8211; wir wollen nun erst einmal diesen Fehler untersuchen und beheben.<\/li>\n<li>Auf diese Weise arbeiten wir uns durch die Fehler, die im Runtime-Betrieb auftauchen und beheben diese.<\/li>\n<\/ul>\n<p>Optimalerweise f&uuml;gt man einer Anwendung f&uuml;r Runtime jedoch dennoch bei n&auml;chster Gelegenheit eine Fehlerbehandlung zu.<\/p>\n<h2>Untersuchen, welche Prozedur vermutlich den Fehler ausl&ouml;st<\/h2>\n<p>Damit kommen wir zum ersten Schritt. Wir f&uuml;gen einer Prozedur den folgenden Aufruf hinzu:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>BeispielCall()\r\n     <span style=\"color:blue;\">Call<\/span> WriteCall(\"BeispielCall\", _\r\n         \"mdlErrors\", <span style=\"color:blue;\">True<\/span>)\r\n     ''... weitere Anweisungen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die dadurch aufgerufene Prozedur sieht wie in Listing 1 aus. Sie erwartet den Namen der Prozedur und des Moduls als Parameter sowie einen Parameter namens <b>bolBeginning<\/b>.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>WriteCall(strProcedure<span style=\"color:blue;\"> As String<\/span>, strModule<span style=\"color:blue;\"> As String<\/span>, bolBeginning<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;\">Set<\/span> db = CurrentDb\r\n     db.Execute \"INSERT INTO tblCalls(CallProcedure, CallModule, CallTime, Beginning) VALUES(''\" & strProcedure _\r\n         & \"'', ''\" & strModule & \"'', \" & Format(Now, \"\\#yyyy-mm-dd hh:nn:ss\\#\") & \", \" & CInt(bolBeginning) & \")\", _\r\n         dbFailOnError\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Prozedur zum Schreiben der Informationen zu einem Prozeduraufruf<\/span><\/b><\/p>\n<p>Diesen stellen wir auf True ein, wenn wir den Aufruf von <b>WriteCall <\/b>am Anfang der Prozedur platzieren und auf <b>False<\/b>, wenn wir das Prozedurende verwenden.<\/p>\n<p>Die Tabelle, die wir mit der <b>INSERT INTO<\/b>-Anweisung f&uuml;llen wollen, finden wir in der Entwurfsansicht in Bild 2.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_05\/pic_1513_002.png\" alt=\"Entwurf der Tabelle f&uuml;r die Aufrufe\" width=\"599,559\" height=\"407,0737\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Entwurf der Tabelle f&uuml;r die Aufrufe<\/span><\/b><\/p>\n<p>Wenn wir den Aufruf der Prozedur <b>WriteCall<\/b> in einige Prozeduren geschrieben und die Datenbank ausgef&uuml;hrt haben, sieht ihr Inhalt beispielsweise wie in Bild 3 aus. Nun stellt sich noch die Frage: Wie bekommen wir den Aufruf schnell in alle Prozeduren?<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_05\/pic_1513_003.png\" alt=\"Tabelle mit einigen aufgezeichneten Prozedur-Calls\" width=\"599,559\" height=\"220,5274\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Tabelle mit einigen aufgezeichneten Prozedur-Calls<\/span><\/b><\/p>\n<h2>Aufruf der Prozedur WriteCall in alle Routine schreiben<\/h2>\n<p>Wir haben bereits davon gesprochen, dass unsere Anwendung recht viele Prozeduren hat. Wie also sollen wir mit vertretbarem Aufwand den Aufruf der Prozedur <b>WriteCall <\/b>in alle Routinen der Anwendungen schreiben?<\/p>\n<p>Logisch: Copy und Paste nimmt uns bereits eine Menge Arbeit ab, und wenn wir den Namen des Moduls bereits vorab eintragen, brauchen wir nach dem Kopieren und Einf&uuml;gen nur noch den Namen der jeweiligen Routine hinzuzuf&uuml;gen:<\/p>\n<pre><span style=\"color:blue;\">Call<\/span> WriteCall(\"BeispielCall\", \"mdlErrors\", <span style=\"color:blue;\">True<\/span>)<\/pre>\n<p>Allerdings ist das bei ein paar hundert Routinen immer noch ein hoher Zeitaufwand und zweitens w&auml;re diese Vorgehensweise recht fehleranf&auml;llig.<\/p>\n<p>Also programmieren wir uns eine eigene Prozedur, mit der wir diese Zeile zu allen Prozeduren hinzuf&uuml;gen. Mit dieser greifen wir auf das VBA-Projekt der aktuellen Datenbank zu, also brauchen wir das entsprechende Objektmodell. Dieses holen wir uns durch Setzen des Verweises aus Bild 4 hinzu.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_05\/pic_1513_004.png\" alt=\"Verweis auf die Bibliothek Microsoft Visual Basic for Applications 5.3\" width=\"499,5589\" height=\"393,8207\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Verweis auf die Bibliothek Microsoft Visual Basic for Applications 5.3<\/span><\/b><\/p>\n<p>Danach programmieren wir die Prozedur aus Listing 2. Diese referenziert zun&auml;chst das aktuelle VBA-Projekt und speichert es in <b>objVBProject<\/b>. Das VBA-Projekt enth&auml;lt f&uuml;r jedes Modul, egal ob Standard- oder Klassenmodul, ein <b>VBComponent<\/b>-Objekt. Diese durchlaufen wir in einer <b>For Each<\/b>-Schleife &uuml;ber alle Elemente der Auflistung <b>VBComponents<\/b>.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>AddCallWriter()\r\n     <span style=\"color:blue;\">Dim <\/span>objVBProject<span style=\"color:blue;\"> As <\/span>VBIDE.VBProject\r\n     <span style=\"color:blue;\">Dim <\/span>objVBComponent<span style=\"color:blue;\"> As <\/span>VBIDE.VBComponent\r\n     <span style=\"color:blue;\">Dim <\/span>objCodeModule<span style=\"color:blue;\"> As <\/span>VBIDE.CodeModule\r\n     <span style=\"color:blue;\">Dim <\/span>lngLine<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strProcedure<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>intProcType<span style=\"color:blue;\"> As <\/span>vbext_ProcKind\r\n     <span style=\"color:blue;\">Dim <\/span>lngProcBodyLine<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngCountOfLines<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strNewLine<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strModule<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>bolGeaendert<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> objVBProject = VBE.ActiveVBProject\r\n     For Each objVBComponent In objVBProject.VBComponents\r\n         <span style=\"color:blue;\">Set<\/span> objCodeModule = objVBComponent.CodeModule\r\n         strModule = objVBComponent.Name\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> strModule = \"mdlErrors\"<span style=\"color:blue;\"> Then<\/span>\r\n             strProcedure = \"\"\r\n             lngCountOfLines = objCodeModule.CountOfLines\r\n             bolGeaendert = <span style=\"color:blue;\">False<\/span>\r\n             For lngLine = 1 To lngCountOfLines\r\n                 <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> strProcedure = objCodeModule.ProcOfLine(lngLine, intProcType)<span style=\"color:blue;\"> Then<\/span>\r\n                     strProcedure = objCodeModule.ProcOfLine(lngLine, intProcType)\r\n                     lngProcBodyLine = objCodeModule.ProcBodyLine(strProcedure, intProcType)\r\n                     strNewLine = \"    <span style=\"color:blue;\">Call<\/span> WriteCall(\"\"\" & strProcedure & \"\"\", \"\"\" & strModule & \"\"\", <span style=\"color:blue;\">True<\/span>)\"\r\n                     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> objCodeModule.Lines(lngProcBodyLine + 1, 1) = strNewLine<span style=\"color:blue;\"> Then<\/span>\r\n                         objCodeModule.InsertLines lngProcBodyLine + 1, strNewLine\r\n                         bolGeaendert = <span style=\"color:blue;\">True<\/span>\r\n                     <span style=\"color:blue;\">End If<\/span>\r\n                     lngCountOfLines = objCodeModule.CountOfLines\r\n                 <span style=\"color:blue;\">End If<\/span>\r\n             <span style=\"color:blue;\">Next<\/span> lngLine\r\n             <span style=\"color:blue;\">If <\/span>bolGeaendert = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n                 On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n                 DoCmd.Close acForm, <span style=\"color:blue;\">Replace<\/span>(strModule, \"Report_\", \"\"), acSaveYes\r\n                 DoCmd.Close acForm, <span style=\"color:blue;\">Replace<\/span>(strModule, \"Form_\", \"\"), acSaveYes\r\n                 DoCmd.RunCommand acCmdSave\r\n                 DoEvents\r\n                 <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Next<\/span> objVBComponent\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Prozedur zum Hinzuf&uuml;gen des Aufrufs von AddCallWriter in alle Prozeduren<\/span><\/b><\/p>\n<p>Uns interessiert das im <b>VBComponent<\/b>-Element enthaltene <b>CodeModule<\/b>-Objekt, das wir mit <b>objCodeModule <\/b>referenzieren. In <b>strModul <\/b>speichern wir den Namen dieses Moduls. Die folgenden Schritte sollen f&uuml;r alle Module mit Ausnahme des aktuellen Moduls namens <b>mdlErrors <\/b>ausgef&uuml;hrt werden.<\/p>\n<p>Wir durchlaufen nun alle Zeilen und identifizieren jeweils die erste Zeile der n&auml;chsten Prozedur. Dazu stellen wir <b>strProcedure <\/b>zun&auml;chst auf eine leere Zeichenkette ein. Dann ermitteln wir die Anzahl der Zeilen des Moduls und speichern diese in <b>lngCountOfLines<\/b>. Die <b>Boolean<\/b>-Variable <b>bolGeaendert<\/b>, die angeben soll, ob wir in dem Modul mindestens eine Zeile ge&auml;ndert haben, stellen wir zuerst auf <b>False <\/b>ein.<\/p>\n<p>Dann durchl&auml;uft die Prozedur alle Zeilen von <b>1 <\/b>bis <b>lngCountOfLines <\/b>in einer <b>For&#8230;Next<\/b>-Schleife. Dabei pr&uuml;ft sie jeweils, ob der in <b>strProcedure <\/b>gespeicherte Prozedurname mit dem Namen der Prozedur aus der aktuellen Zeile &uuml;bereinstimmt. Ist das der Fall, befinden wir uns noch in der aktuellen Prozedur (oder, wenn wir uns am Modulanfang befinden, im Deklarationsbereich &#8211; hier enth&auml;lt <b>strProcedure <\/b>immer noch eine leere Zeichenkette).<\/p>\n<p>Den Namen der Prozedur, zu der die aktuelle Zeile geh&ouml;rt, ermitteln wir mit der Funktion <b>ProcOfLine<\/b>. Dieser &uuml;bergeben wir die Zeilennummer und eine Variable, die den Typ der Prozedur entgegennimmt. Der Typ interessiert uns hier nicht, aber wir m&uuml;ssen die Variable zwingend als Parameter setzen. Die Funktion liefert schlie&szlig;lich den Namen der aktuellen Routine zur&uuml;ck. Stimmt dieser nicht mit dem Wert aus <b>strProcedure <\/b>&uuml;berein, ist dies die erste Zeile der neuen Routine.<\/p>\n<p>Dann ermitteln wir die tats&auml;chliche erste Zeile der aktuellen Prozedur. Es kann n&auml;mlich auch sein, dass sich in der mit <b>lngLine<\/b> ermittelten Zeile eine leere Zeile vor der eigentlichen Prozedur befindet. Um die Kopfzeile der Prozedur zu erhalten, verwenden wir die Funktion <b>ProcBodyLine<\/b>, der wir den Namen der Prozedur und wieder die Variable f&uuml;r den Typ der Prozedur &uuml;bergeben. Das Ergebnis speichern wir in <b>lngProcBodyLine<\/b>.<\/p>\n<p>Wozu ben&ouml;tigen wir diese Zeile? Weil wir genau hinter dieser Zeile, also als erste Anweisung der Prozedur, unsere Anweisung <b>Call WriteCall&#8230; <\/b>platzieren wollen. Diese schreiben wir zuerst in die Variable <b>strNewLine<\/b>. Dazu f&uuml;gen wir eine Zeichenkette bestehend aus <b>Call WriteCall <\/b>und dem Namen der Prozedur und des Moduls aus den Variablen <b>strProcedure <\/b>und <b>strModule <\/b>zusammen.<\/p>\n<p>Nun pr&uuml;fen wir, ob die erste Anweisung nicht bereits der Aufruf der Prozedur <b>WriteCall <\/b>ist. Dazu vergleichen wir die mit der <b>Lines<\/b>-Funktion mit dem Parameter <b>lngProcBodyLine + 1 <\/b>ermittelte Zeile mit der Zeile aus <b>strNewLine<\/b>. Warum <b>+ 1<\/b>? Weil wir nicht die Kopfzeile, sondern die erste Anweisung pr&uuml;fen wollen. <\/p>\n<p>Finden wir hier nicht die Zeile <b>Call WriteCall&#8230;<\/b>, f&uuml;gen wir diese nun mit der <b>InsertLines<\/b>-Methode an der entsprechenden Stelle ein.<\/p>\n<p>Au&szlig;erdem stellen wir den Wert der Variablen <b>bolGeaendert <\/b>auf <b>True <\/b>ein. Schlie&szlig;lich passen wir den Wert der Laufvariablen <b>lngCountOfLines <\/b>an, da sich die Anzahl der Zeilen des Moduls ge&auml;ndert hat, da wir eine Zeile hinzugef&uuml;gt haben. Wenn wir dies f&uuml;r viele Zeilen erledigen, kann es sein, dass wir mit dem urspr&uuml;nglichen Wert f&uuml;r <b>lngCountOfLines <\/b>in der Schleife nicht mehr alle Zeilen erfassen k&ouml;nnen.<\/p>\n<p>Auf diese Weise durchlaufen wir alle Zeilen und somit alle Routinen und f&uuml;gen diesen jeweils einen Aufruf der Prozedur <b>WriteCall <\/b>hinzu.<\/p>\n<p>Wenn <b>bolGeaendert <\/b>danach den Wert <b>True <\/b>hat, wollen wir die &Auml;nderungen an dem jeweiligen Objekt speichern. Um nicht einzeln ermitteln zu m&uuml;ssen, ob es sich um ein Standardmodul, ein alleinstehendes Klassenmodul oder ein Klassenmodul eines Formulars oder Berichts handelt, schlie&szlig;en und speichern wir bei deaktivierter Fehlerbehandlung einfach jeweils das Formular und den Bericht mit dem Namen, dem wir die Zeichenfolge <b>Form_ <\/b>beziehungsweise <b>Report_ <\/b>entnommen haben. Beim dazu verwenden Aufruf der <b>DoCmd.Close<\/b>-Methode haben wir den zweiten Parameter jeweils auf <b>acSaveYes <\/b>eingestellt, damit das jeweilige Objekt immer direkt gespeichert wird. Anderenfalls kann es, wenn mehrere hundert Objekte ge&ouml;ffnet sind, zu Fehlern kommen. Schlie&szlig;lich sorgen wir mit der <b>DoEvents<\/b>-Anweisung noch daf&uuml;r, dass der Bildschirm nicht einfriert und das Ergebnis aller Aktionen direkt sichtbar ist.<\/p>\n<p>Auf diese Weise durchlaufen wir alle Module des VBA-Projekts.<\/p>\n<p>In der Folge enth&auml;lt jede Routine den Aufruf der Prozedur <b>WriteCall <\/b>und wir k&ouml;nnen die Datenbank auf dem Runtime-Rechner erneut ausprobieren, um die erste fehlerausl&ouml;sende Prozedur zu finden.<\/p>\n<h2>WriteCall-Aufrufe wieder entfernen<\/h2>\n<p>Auf die gleiche Weise k&ouml;nnen wir die Aufrufe der <b>WriteCall<\/b>-Prozedur auch wieder entfernen. Die Prozedur <b>RemoveCallWriter <\/b>sieht wie in Listing 3 aus und erledigt eine &auml;hnliche Abfolge von Schritten. Allerdings durchsuchen wir nicht mehr explizit alle Routinen einzeln, sondern wir durchlaufen einfach alle Zeilen des Moduls und pr&uuml;fen, ob eine Zeile zu Beginn die Zeichenfolge <b>Call WriteCall <\/b>enth&auml;lt. In diesem Fall l&ouml;schen wir die gefundene Zeile mit der Methode <b>DeleteLines<\/b>. Dieser &uuml;bergeben wir mit dem ersten Parameter die erste zu l&ouml;schende Zeile und mit der zweiten die Anzahl der zu l&ouml;schenden Zeilen, in diesem Fall eine. Danach sollte das Projekt wieder aussehen wir zuvor.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>RemoveCallWriter()\r\n     <span style=\"color:blue;\">Dim <\/span>objVBProject<span style=\"color:blue;\"> As <\/span>VBIDE.VBProject\r\n     <span style=\"color:blue;\">Dim <\/span>objVBComponent<span style=\"color:blue;\"> As <\/span>VBIDE.VBComponent\r\n     <span style=\"color:blue;\">Dim <\/span>objCodeModule<span style=\"color:blue;\"> As <\/span>VBIDE.CodeModule\r\n     <span style=\"color:blue;\">Dim <\/span>lngLine<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strProcedure<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>intProcType<span style=\"color:blue;\"> As <\/span>vbext_ProcKind\r\n     <span style=\"color:blue;\">Dim <\/span>lngCountOfLines<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strModule<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>bolGeaendert<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> objVBProject = VBE.ActiveVBProject\r\n     For Each objVBComponent In objVBProject.VBComponents\r\n         <span style=\"color:blue;\">Set<\/span> objCodeModule = objVBComponent.CodeModule\r\n         strModule = objVBComponent.Name\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> strModule = \"mdlErrors\"<span style=\"color:blue;\"> Then<\/span>\r\n             strProcedure = \"\"\r\n             lngCountOfLines = objCodeModule.CountOfLines\r\n             bolGeaendert = <span style=\"color:blue;\">False<\/span>\r\n             For lngLine = 1 To lngCountOfLines\r\n                 <span style=\"color:blue;\">If <\/span>left(<span style=\"color:blue;\">Trim<\/span>(objCodeModule.Lines(lngLine, 1)), 14) = \"<span style=\"color:blue;\">Call<\/span> WriteCall\"<span style=\"color:blue;\"> Then<\/span>\r\n                     objCodeModule.DeleteLines lngLine\r\n                     bolGeaendert = <span style=\"color:blue;\">True<\/span>\r\n                 <span style=\"color:blue;\">End If<\/span>\r\n             <span style=\"color:blue;\">Next<\/span> lngLine\r\n             <span style=\"color:blue;\">If <\/span>bolGeaendert = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n                 On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n                 DoCmd.Close acForm, <span style=\"color:blue;\">Replace<\/span>(strModule, \"Report_\", \"\"), acSaveYes\r\n                 DoCmd.Close acForm, <span style=\"color:blue;\">Replace<\/span>(strModule, \"Form_\", \"\"), acSaveYes\r\n                 DoCmd.RunCommand acCmdSave\r\n                 DoEvents\r\n                 <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Next<\/span> objVBComponent\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Prozedur zum Entfernen des Aufrufs von WriteCall aus allen Prozeduren<\/span><\/b><\/p>\n<h2>Fehlerhaftes Modul ermitteln<\/h2>\n<p>Mit dem Start dieser Version des VBA-Projekts auf dem Runtime-Rechner k&ouml;nnen wir uns nun in der Tabelle <b>tblCalls <\/b>ausgeben lassen, welche Prozeduren aus welchem Modul wann aufgerufen werden. Da wir auch das Datum und die Uhrzeit speichern, k&ouml;nnen wir so auch erkennen, welche Prozeduren besonders lange brauchen, falls wir einmal ein Performance-Problem untersuchen wollen.<\/p>\n<p>Haupts&auml;chlich aber wollen wir nun ermitteln, welche Routine in der Runtime einen Fehler ausl&ouml;st. Das ist potenziell die Routine, die als letzte in die Tabelle <b>tblCalls<\/b> eingetragen wurde.<\/p>\n<h2>Problem bei Tests aufgetaucht<\/h2>\n<p>Die wenigsten Prozeduren sind direkt perfekt, so auch die zum Anlegen der Zeile zum Aufruf der Prozedur <b>WriteCall<\/b> nicht. Wir schreiben darin etwas naiv immer in die erste Zeile nach der Kopfzeile der Routine. Es kann jedoch auch sein, dass die Kopfzeile durch den Unterstrich unterbrochen und in der folgenden Zeile fortgesetzt wird. In diesem Fall wird der Aufruf von <b>WriteCall <\/b>inmitten der Kopfzeile geschrieben, was in der Folge zu Fehlern f&uuml;hrt. Also haben wir <b>AddCallWriter <\/b>nochmals geringf&uuml;gig umgebaut, indem wir mit einer Funktion namens <b>GetFirstStatementLineNumber <\/b>die tats&auml;chliche Zeilennummer der ersten Anweisung ermitteln (siehe Listing 4).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>GetFirstStatementLineNumber(objCodeModule<span style=\"color:blue;\"> As <\/span>VBide.CodeModule, lngProcBodyLine<span style=\"color:blue;\"> As Long<\/span>)<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strLine<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngFirstStatementLineNumber<span style=\"color:blue;\"> As Long<\/span>\r\n     lngFirstStatementLineNumber = lngProcBodyLine\r\n     strLine = objCodeModule.Lines(lngFirstStatementLineNumber, 1)\r\n     strLine = <span style=\"color:blue;\">Trim<\/span>(strLine)\r\n     <span style=\"color:blue;\">Do While<\/span> right(strLine, 1) = \"_\"\r\n         lngFirstStatementLineNumber = lngFirstStatementLineNumber + 1\r\n         strLine = objCodeModule.Lines(lngFirstStatementLineNumber, 1)\r\n         strLine = <span style=\"color:blue;\">Trim<\/span>(strLine)\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     GetFirstStatementLineNumber = lngFirstStatementLineNumber + 1\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Ermitteln der ersten Anweisung innerhalb einer Prozedur<\/span><\/b><\/p>\n<p>Diese erwartet das CodeModule und die Nummer der ersten Zeile der Routine als Parameter. Sie schreibt die Zeilennummer in die Variable <b>lngFirstStatementLineNumber<\/b>. Sie pr&uuml;ft dann, ob diese Zeile am Ende einen Unterstrich enth&auml;lt. Falls ja, wird <b>lngFirstStatementLineNumber <\/b>um <b>1<\/b> erh&ouml;ht und die n&auml;chste Zeile untersucht &#8211; solange, bis am Ende kein Unterstrich zu finden ist. <b>lngFirstStatementLineNumber<\/b> enth&auml;lt dann die Nummer der ersten Anweisung der Prozedur, die von der Funktion zur&uuml;ckgegeben wird.<\/p>\n<p>In der Prozedur <b>AddCallWriter <\/b>lesen wir diesen Wert in die Variable <b>lngFirstStatementLineNumber <\/b>innerhalb der Schleife ein und verwenden diesen, um die Zeile an der richtigen Stelle einzuf&uuml;gen:<\/p>\n<pre>If <span style=\"color:blue;\">Not<\/span> objCodeModule.Lines(lngFirstStatementLineNumber, 1)  _\r\n         = strNewLine Then\r\n     objCodeModule.InsertLines _\r\n         lngFirstStatementLineNumber, strNewLine\r\n     ...<\/pre>\n<h2>Routine mit Fehlerbehandlung ausstatten<\/h2>\n<p>Diese Routine statten wir nun mit einer minimalen Fehlerbehandlungsroutine aus. Dabei wollen wir den Fehler gar nicht beheben, sondern einfach nur die Fehlernummer, die Fehlermeldung, die betroffene Prozedur und das Modul sowie die Zeilenzahl in eine weitere Tabelle namens <b>tblErrors <\/b>schreiben (siehe Bild 5). Wichtig ist hier, dass wir die eventuell in <b>Err.Description <\/b>enthaltenen Hochkommata durch doppelte Hochkommata ersetzen. Sonst l&ouml;st diese Anweisung selbst wieder einen unbehandelten Fehler aus und es landen keine Fehlerinformationen in der Tabelle <b>tblErrors<\/b>.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_05\/pic_1513_005.png\" alt=\"Tabelle zum Speichern von Fehlerinformationen\" width=\"499,5589\" height=\"404,4048\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Tabelle zum Speichern von Fehlerinformationen<\/span><\/b><\/p>\n<p>Um den Fehler zu speichern, verwenden wir eine &auml;hnliche Prozedur wie zum Speichern der Prozeduraufrufe. Diese sieht wie in Listing 5 aus.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>HandleError(lngNumber<span style=\"color:blue;\"> As Long<\/span>, strMessage<span style=\"color:blue;\"> As String<\/span>, lngLine<span style=\"color:blue;\"> As Long<\/span>, strProcedure<span style=\"color:blue;\"> As String<\/span>, _\r\n         strModule<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     db.Execute \"INSERT INTO tblErrors(ErrorNumber, ErrorMessage, ErrorLine, ErrorProcedure, ErrorModule) VALUES(\" _\r\n         & lngNumber & \", ''\" & <span style=\"color:blue;\">Replace<\/span>(strMessage, \"''\", \"''''\") & \"'', \" & lngLine & \", ''\" & strProcedure & \"'', ''\" _\r\n         & strModule & \"'')\", dbFailOnError\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Prozedur zum Schreiben von Fehlerinformationen in die Tabelle tblErrors<\/span><\/b><\/p>\n<p>Diese Prozedur muss nun an geeigneter Stelle aufgerufen werden. Dazu f&uuml;gen wir einer bestehenden Routine mehrere Elemente hinzu. Als Erstes ben&ouml;tigen wir die Anweisung <b>On Error Goto Errorhandling<\/b>, die wir ganz oben in der Prozedur einbauen. Sie sorgt daf&uuml;r, dass wir bei einem Fehler zur Sprungmarke <b>Errorhandling <\/b>springen (siehe Listing 6).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>Test_HandleError()\r\n10    <span style=\"color:blue;\">On Error GoTo<\/span> Errorhandling\r\n20    <span style=\"color:blue;\">Debug.Print<\/span> 1 \/ 0\r\n30    <span style=\"color:blue;\">Exit Sub<\/span>\r\nErrorhandling:\r\n     HandleError Err.Number, Err.Description, Erl, \"Test_HandleError\", \"mdlErrors\"\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Ausl&ouml;sen der Fehlerprozedur<\/span><\/b><\/p>\n<p>Diese platzieren wir am Ende der Routine und f&uuml;gen dahinter den Aufruf der Fehlerprotokollroutine ein.<\/p>\n<p>Vorher platzieren wir noch die Anweisung <b>Exit Sub<\/b>, damit die Fehlerbehandlung nur ausgef&uuml;hrt wir, wenn die Fehlerbehandlung uns explizit dorthin schickt.<\/p>\n<p>Schlie&szlig;lich fehlen noch die Zeilennummern, ohne die wir bei l&auml;ngeren Prozeduren auch kaum eine Chance haben, einen Fehler allein auf Basis der Fehlernummer und der Beschreibung zu finden. Beides wollen wir m&ouml;glichst in einer Prozedur hinzuf&uuml;gen. Diese Prozedur wollen wir jedoch nicht nutzen, um alle Routinen in allen Modulen auf einmal mit einer Fehlerbehandlung auszustatten, sondern wir gehen hier Schritt f&uuml;r Schritt vor &#8211; immerhin wollen wir erst einmal herausfinden, wo in der Anwendung &uuml;berhaupt ein Fehler auftritt.<\/p>\n<p>Dazu haben wir eine Funktion zum Anlegen einer einfachen Fehlerbehandlung wie oben beschrieben erstellt, die wir in einem weiteren Beitrag namens  <b>Fehlerbehandlung per VBA hinzuf&uuml;gen <\/b>(<b>www.access-im-unternehmen.de\/1514<\/b>) vorstellen.<\/p>\n<h2>Anwendung in der Runtime Schritt f&uuml;r Schritt lauff&auml;hig machen<\/h2>\n<p>Diese Fehlerbehandlung f&uuml;gen wir nun in die Prozedur ein, die laut unserer Tabelle <b>tblCalls <\/b>als letzte aufgerufen wurde und die somit in Verdacht steht, den Fehler ausgel&ouml;st zu haben.<\/p>\n<p>Dann kopieren wir die Anwendung wieder auf den Runtime-Rechner und starten diese. Die Fehlermeldung sollte nun nicht an der gleichen Stelle erscheinen &#8211; entweder sie erfolgt sp&auml;ter oder sie bleibt vollst&auml;ndig aus.<\/p>\n<p>Dann schlie&szlig;en wir die Anwendung auf dem Runtime-Rechner und kopieren sie wieder auf unseren Entwicklungsrechner. Hier &ouml;ffnen wir diese nun und schauen uns den Inhalt der beiden Tabellen <b>tblCalls <\/b>und <b>tblErrors <\/b>an.<\/p>\n<p>Anhand eines gegebenenfalls neuen Eintrags in der Tabelle <b>tblErrors <\/b>k&ouml;nnen wir nun gezielt auf Fehlersuche gehen &#8211; immerhin sollten wir nun mit einer Fehlermeldung und dem Ort des Fehlers ausgestattet sein.<\/p>\n<p>Wir versuchen, den Fehler zu beheben und starten dann einen erneuten Durchlauf. Auf diese Weise beheben wir alle Probleme, die in der Runtime auftauchen.<\/p>\n<h2>Wenn es hart auf hart kommt<\/h2>\n<p>Es gibt Situationen, wo sich aus unterschiedlichen Gr&uuml;nden kein Fehler zeigt. In diesem Fall m&uuml;ssen wir dennoch einmal Hand anlegen. Immerhin sollte die Tabelle <b>tblCalls <\/b>die Prozedur zeigen, die zuletzt aufgerufen wurde. Ist das der Fall, schauen wir uns diese Prozedur genauer an.<\/p>\n<h2>Fehlerhafte Zeilen direkt anzeigen<\/h2>\n<p>Wenn wir die Fehler wie oben beschrieben in der Fehlertabelle gespeichert haben, k&ouml;nnen wir diese auch nutzen, um die fehlerhafte Zeile im VBA-Editor schnell anzuzeigen. Dazu nutzen wir die L&ouml;sung, die wir im Beitrag <b>Fehlerhafte Zeilen anzeigen lassen <\/b>(<b>www.access-im-unternehmen.de\/1518<\/b>) erl&auml;utert haben.<\/p>\n<p>Dieser Beitrag stellt ein Formular vor, mit dem die in der Tabelle <b>tblErrors <\/b>gespeicherten Fehler in einem Formular angezeigt werden k&ouml;nnen. Durch Markieren und Anklicken k&ouml;nnen wir direkt zur fehlerhaften Stelle im VBA-Editor springen.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Die hier vorgestellten Techniken unterst&uuml;tzen Sie dabei, Fehler unter der Runtime-Version von Access zu entdecken. Dabei m&uuml;ssen wir nicht direkt die vollst&auml;ndige Anwendung mit einer Fehlerbehandlung ausstatten (was davon abgesehen aber bei n&auml;chster Gelegenheit erledigt werden sollte), sondern ermitteln zun&auml;chst einmal, in welcher Prozedur &uuml;berhaupt ein Fehler auftaucht, der zu der f&uuml;r die Runtime typische Fehlermeldung ohne genaue Informationen f&uuml;hrt.<\/p>\n<p>Danach statten wir diese Prozedur mit einer Fehlerbehandlung und Zeilennummern aus, um herauszufinden, welcher Fehler in welcher Zeile auftritt.<\/p>\n<p>Diesen beheben wir in der Vollversion, kopieren die Anwendung dann wieder auf den Runtime-Rechner und testen erneut &#8211; solange, bis keine Fehler mehr vorhanden sind.<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>FehlerInDerRuntimeFinden.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/009FD3B7-8034-4C08-AFFF-E2E74D7AB907\/aiu_1513.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wenn wir eine Datenbank, die unter der Vollversion von Access fehlerfrei l&auml;uft, in der Runtime &ouml;ffnen, kann es zu unerkl&auml;rlichen Fehlern kommen. Die Runtime verabschiedet sich dann in der Regel mit einer Meldung wie &#8222;Die Ausf&uuml;hrung dieser Anwendung wurde wegen eines Laufzeitfehlers angehalten.&#8220; Damit k&ouml;nnen wir nat&uuml;rlich erst einmal nicht viel anfangen. Anlass zu diesem Beitrag ist ein konkretes Problem einer Kundin, deren Anwendung auf der Runtime-Version von Access nicht wie gew&uuml;nscht funktioniert. Beim Testen der Anwendung in der Runtime kam ich jedoch gar nicht erst soweit wie die Kundin. Es tauchte bereits vorher die besagte Meldung auf. Wie k&ouml;nnen wir nun herausfinden, was genau den Fehler verursacht und welche Zeile ihn ausl&ouml;st? Vorgehensweisen dazu stellen wir in diesem Beitrag vor.<\/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":[662024,66052024,44000013],"tags":[],"class_list":["post-55001513","post","type-post","status-publish","format-standard","hentry","category-662024","category-66052024","category-Programmiertechnik"],"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>Fehler in der Runtime von Access finden - 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\/Fehler_in_der_Runtime_von_Access_finden\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Fehler in der Runtime von Access finden\" \/>\n<meta property=\"og:description\" content=\"Wenn wir eine Datenbank, die unter der Vollversion von Access fehlerfrei l&auml;uft, in der Runtime &ouml;ffnen, kann es zu unerkl&auml;rlichen Fehlern kommen. Die Runtime verabschiedet sich dann in der Regel mit einer Meldung wie &quot;Die Ausf&uuml;hrung dieser Anwendung wurde wegen eines Laufzeitfehlers angehalten.&quot; Damit k&ouml;nnen wir nat&uuml;rlich erst einmal nicht viel anfangen. Anlass zu diesem Beitrag ist ein konkretes Problem einer Kundin, deren Anwendung auf der Runtime-Version von Access nicht wie gew&uuml;nscht funktioniert. Beim Testen der Anwendung in der Runtime kam ich jedoch gar nicht erst soweit wie die Kundin. Es tauchte bereits vorher die besagte Meldung auf. Wie k&ouml;nnen wir nun herausfinden, was genau den Fehler verursacht und welche Zeile ihn ausl&ouml;st? Vorgehensweisen dazu stellen wir in diesem Beitrag vor.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2024-08-27T08:47:18+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg02.met.vgwort.de\/na\/1d542d5e076047b68450622e9d9da024\" \/>\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=\"19\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Fehler in der Runtime von Access finden\",\"datePublished\":\"2024-08-27T08:47:18+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/\"},\"wordCount\":3366,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/1d542d5e076047b68450622e9d9da024\",\"articleSection\":[\"2024\",\"5\\\/2024\",\"Programmiertechnik\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/\",\"name\":\"Fehler in der Runtime von Access finden - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/1d542d5e076047b68450622e9d9da024\",\"datePublished\":\"2024-08-27T08:47:18+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/1d542d5e076047b68450622e9d9da024\",\"contentUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/1d542d5e076047b68450622e9d9da024\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Fehler_in_der_Runtime_von_Access_finden\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Fehler in der Runtime von Access finden\"}]},{\"@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":"Fehler in der Runtime von Access finden - 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\/Fehler_in_der_Runtime_von_Access_finden\/","og_locale":"de_DE","og_type":"article","og_title":"Fehler in der Runtime von Access finden","og_description":"Wenn wir eine Datenbank, die unter der Vollversion von Access fehlerfrei l&auml;uft, in der Runtime &ouml;ffnen, kann es zu unerkl&auml;rlichen Fehlern kommen. Die Runtime verabschiedet sich dann in der Regel mit einer Meldung wie \"Die Ausf&uuml;hrung dieser Anwendung wurde wegen eines Laufzeitfehlers angehalten.\" Damit k&ouml;nnen wir nat&uuml;rlich erst einmal nicht viel anfangen. Anlass zu diesem Beitrag ist ein konkretes Problem einer Kundin, deren Anwendung auf der Runtime-Version von Access nicht wie gew&uuml;nscht funktioniert. Beim Testen der Anwendung in der Runtime kam ich jedoch gar nicht erst soweit wie die Kundin. Es tauchte bereits vorher die besagte Meldung auf. Wie k&ouml;nnen wir nun herausfinden, was genau den Fehler verursacht und welche Zeile ihn ausl&ouml;st? Vorgehensweisen dazu stellen wir in diesem Beitrag vor.","og_url":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/","og_site_name":"Access im Unternehmen","article_published_time":"2024-08-27T08:47:18+00:00","og_image":[{"url":"http:\/\/vg02.met.vgwort.de\/na\/1d542d5e076047b68450622e9d9da024","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"19\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Fehler in der Runtime von Access finden","datePublished":"2024-08-27T08:47:18+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/"},"wordCount":3366,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/#primaryimage"},"thumbnailUrl":"http:\/\/vg02.met.vgwort.de\/na\/1d542d5e076047b68450622e9d9da024","articleSection":["2024","5\/2024","Programmiertechnik"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/","url":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/","name":"Fehler in der Runtime von Access finden - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/#primaryimage"},"thumbnailUrl":"http:\/\/vg02.met.vgwort.de\/na\/1d542d5e076047b68450622e9d9da024","datePublished":"2024-08-27T08:47:18+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/#primaryimage","url":"http:\/\/vg02.met.vgwort.de\/na\/1d542d5e076047b68450622e9d9da024","contentUrl":"http:\/\/vg02.met.vgwort.de\/na\/1d542d5e076047b68450622e9d9da024"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Fehler_in_der_Runtime_von_Access_finden\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Fehler in der Runtime von Access finden"}]},{"@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\/55001513","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=55001513"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001513\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001513"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001513"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001513"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}