Begin Sequence ... Recover ...

Alle Fragen um die Programmierung, die sich sonst nicht kategorisieren lassen. Von Makro bis Codeblock, von IF bis ENDIF

Moderator: Moderatoren

Antworten
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Begin Sequence ... Recover ...

Beitrag von Jan »

Das ist ja nun schon seit Clipper-Zeiten ein Werkzeug zum Abfangen von Laufzeitfehlern. Aber wer von Euch setzt das im realen Entwickler-Leben wirklich noch ein? Wenn ja, in welchen Situationen? Wenn nein, was nehmt Ihr statt dessen?

Warum ich das frage: Ich benutze das eher sparsam. Was aber eher daran liegt das sich mir die Tiefen noch nicht ergründet haben. Was man damit erreichen könnte wenn man es denn wirklich verstünde. Und ich frage mich in dem Zuge halt auch, ob das mit heutigen Programmierstrategien wirklich noch sinnvoll ist, oder ob man die Funktionalitäten nicht besser anders umsetzen sollte.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
Manfred
Foren-Administrator
Foren-Administrator
Beiträge: 21165
Registriert: Di, 29. Nov 2005 16:58
Wohnort: Kreis Wesel
Hat sich bedankt: 206 Mal
Danksagung erhalten: 67 Mal

Re: Begin Sequence ... Recover ...

Beitrag von Manfred »

Ich benutze es. Und ich habe es auch in meinem übernommenen Projekt drin und da wird es hauptsächlich ähnlich wie Return to Master genutzt. Sprich wenn man innerhalb BEGIn Sequence Funktionen aufruft und dann irgendwann tiefer ein BEAk macht, dann wird direkt alles komplett verlassen.
Gruß Manfred
Mitglied der XUG Osnabrück
Schatzmeister des Deutschsprachige Xbase-Entwickler e.V.
großer Fan des Xbaseentwicklerwiki https://wiki.xbaseentwickler.de/index.p ... Hauptseite
Doof kann man sein, man muß sich nur zu helfen wissen!!
Benutzeravatar
Herbert
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 1991
Registriert: Do, 14. Aug 2008 0:22
Wohnort: Gmunden am Traunsee, Österreich
Danksagung erhalten: 3 Mal
Kontaktdaten:

Re: Begin Sequence ... Recover ...

Beitrag von Herbert »

Hallo Jan
Du kannst nur soweit Fehler abfangen, wie die von dir benutzten Funktionen auch eine entsprechende Fehlerrückgabe liefern. Fehlt dies oder rufst du Funktionen auf, die nach aussen kommunizieren, wie z.B. Active-X-Aufrufe, so hilft dir das Sequence-Konstrukt, den externen Fehlercode festzuhalten. Dazu verhinderst du den Knall, dass dein Programm nach der Fehlermeldung abtaucht.
Grüsse Herbert
Immer in Bewegung...
Benutzeravatar
Tom
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 9345
Registriert: Do, 22. Sep 2005 23:11
Wohnort: Berlin
Hat sich bedankt: 100 Mal
Danksagung erhalten: 359 Mal
Kontaktdaten:

Re: Begin Sequence ... Recover ...

Beitrag von Tom »

Es gibt einige grundsätzliche Situationen, in denen ich dieses Konstrukt verwende.

1. Im Web-Server (FTP, HTTP, SOAP, propreitäre Protokolle). Der erzeugt je Verbindung eine Server-Instanz, die in einem eigenen Thread läuft in eine Sequenz eingebettet ist. Tritt hier ein Fehler auf, beendet sich diese Serverinstanz, ohne dass das Programm abschmiert. Die Fehlersituationen werden protokolliert. Damit fangen wir u.a. falsch parametrisierte Abfragen ab, Timeouts und ähnliches (Zugriff auf Tabellen nicht möglich). Trotzdem läuft die Serversoftware zuweilen jahrelang ohne Unterbrechung. Als Rückgabe entsteht ein Fehlercode - abhängig vom verwendeten Protokoll.

2. In einem Servicetool, das Datensicherungen und Konsistenzprüfungen und ähnliches vornimmt, Updates lädt und verteilt usw. Auch das verhält sich entsprechend.

3. In bestimmten Callbacks/Slots, die erfahrungsgemäß Synchronisierungsprobleme haben, etwa XbpBrowse:ForceStable im Kontext des Ownerdrawings (das manchmal langsamer als der UI-Thread ist). Hier ersparen wir uns z.B. auch bei der Ermittlung von Zellenfarben die Prüfung der benutzererfassten Daten. Schmiert die in eine Sequenz eingebettete Auswertung der dazugehörigen Codeblöcke ab, defaultet das System schlicht auf die Standardfarben.

4. Beim Direktexport z.B. nach Excel. Situationen wie bereits geöffnete Excel-Dateien oder schlicht fehlende Excel-Instanzen lassen sich leicht abfangen, doch eine Trialversion von Office verhält sich sehr eigenartig, lässt aber die Initialisierung des Controls zu. Es ist sehr schwer, auf alle denkbaren Fehlersituationen zu reagieren, aber es ist blöd, wenn daraus Laufzeitfehler entstehen. Eine Sequenz erlaubt den relativ eleganten Umgang mit dieser Situation.

5. Beim Umgang mit Datenbankservern und ODBC-Datenquellen in Xbase-Notation. Das merzen wir zwar derzeit gerade aus, aber es wird noch an einigen Stellen verwendet. Statt alle denkbaren Client-Errors abzufangen, die ohnehin selten umgangen werden können, fällt das System an dieser Stelle auf die Notfallebene, wenn ein Laufzeitfehler generiert wird. Es merkt sich schlicht die Vorgänge und versucht sie später abermals.

6. Simpel: Beim Versuch, eine DBE zu laden.

7. Im Fehlersystem, wenn es Dateiöffnungsfehler gibt, die Dateien aber vorhanden sind - vor allem im Kontext von (vielen) Indexdateien, die nicht selten Virenscanner in die Knie zwingen, wodurch das Betriebssystem Timeouts generiert.

Und so weiter.

Es ist nicht unelegant oder "old-fashioned", diese Systematik zu verwenden. Ganz im Gegenteil ist das ein mächtiges Tool, um Situationen abzufangen, die sich der Vorhersehbarkeit entziehen, ohne dass man falsch programmiert hätte. Auch ein einfaches "DbUseArea()" kann ja fehlschlagen, obwohl die Tabelle vorhanden ist und theoretisch im Shared-Modus geöffnet werden kann. Aber ein findiger Anwender hat sie manipuliert, oder sie ist physisch beschädigt. Bettet man das also in eine Sequenz ein, kann man in der Anwendung darüber informieren, dass es ein Problem zu geben scheint, ohne den Anwender mit Laufzeitfehlern zu konfrontieren.
Herzlich,
Tom
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15689
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Begin Sequence ... Recover ...

Beitrag von brandelh »

Der DBEDITOR() nutzt das um z.B. Laufzeitfehler bei Öffnen einer Datei abzufangen.
zwischen file(cFile) und use (cFile) könnte ja gerade gelöscht worden sein ... oder schreibschutz ...

Oder Bei selbst gebauten Filterangaben etc. würde beim &(cFilter) ein Laufzeitfehler das Programm beenden.

Das will kein Anwender sehen ;-)

Irgendwo habe ich mal gelesen, dass man früher (DOS ?) zuerst alle Fehler abgeprüft hätte, das wäre aber altmodisch.
Modern wäre es im (seltenen) Laufzeitfehlerfall diesen zu behandeln.
Bei Xbase++ ist das BEGIN SEQUENCE ... anderswo nennt man es TRY ... ;-)
Gruß
Hubert
Benutzeravatar
Tom
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 9345
Registriert: Do, 22. Sep 2005 23:11
Wohnort: Berlin
Hat sich bedankt: 100 Mal
Danksagung erhalten: 359 Mal
Kontaktdaten:

Re: Begin Sequence ... Recover ...

Beitrag von Tom »

Wenn man allerdings zu allgemein wird ("Es gibt ein Problem beim Öffnen der Datei ..."), also einfach alles, was irgendwie Fehler erzeugen könnte, in Sequenzen einbettet, verhindert man andererseits, dass echte Fehlersituationen schnell und ursächlich erkannt werden können. Man sollte sich also genau überlegen, auf welchem Level man das einsetzt.

Das von Hubert genannte Problem mit dem "selbstgebauten" Filter ist gut. Ich mache so etwas in einem Servicetool, das für unsere Supportmitarbeiter programmiert wurde und das u.a. erlaubt, Abfragen (Filter, Suche) auf Tabellen selbst zu formulieren. Da gibt es zwar einen Assistenten, der bei der Generierung solcher Abfragen hilft (Feldauswahl, Abfrage mit Vorgaben abhängig vom Feldtyp), aber man kann auch direkt Abfragen eintippen. Die Evaluation erfolgt dann in einer Sequenz. Scheitert die, gab es einen Syntaxfehler, und ein entsprechender Hinweis erscheint. Danach kann man dann die Abfrage korrigieren. Anders wäre eine Syntaxprüfung ein haarsträubender Aufwand.
Herzlich,
Tom
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Re: Begin Sequence ... Recover ...

Beitrag von Jan »

Hallo,

vielen Dank für Eure Anmerkungen dazu, und die vielen Anwendungsbeispiele. Das dürfte also wohl in der Tat immer noch sehr aktuell sein.

Könnt Ihr mir vielleicht mal zwei oder drei Beispiele posten, wie man sowas sinnnig aufbaut? Wie so häufig beiße ich bei den Alaska-Beispielen in der Doku irgendwie auf Granit. Da fehlt mir jegliche Phantasie der realen Umsetzungsmöglichkeit.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15689
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Begin Sequence ... Recover ...

Beitrag von brandelh »

in der Hilfe zu BEGIN SEQUENCE stehen die zwei wichtigsten.
1. aus tiefer Verschachtelung gezielt mehrere Ebenen herausspringen.
2. eine lokale Fehlerbehandlung, dann muss man aber auch ein Error-Objekt übergeben und auswerten.

Zu Toms Anmerkung, einfach alle Fehler auf "Es ist ein Fehler aufgetreten" zu leiten wäre sicher nicht dienlich ! 8)

Aber so kann man ohne die Errorsys global zu ändern lokal Fehler bearbeiten. Aber NIE den otherwise Zweig für alle anderen Fehler mit Xbase++ Standardbehandlung vergessen.

Wie immer muss man wissen was man macht und warum man es so macht ;-)
Gruß
Hubert
Benutzeravatar
Manfred
Foren-Administrator
Foren-Administrator
Beiträge: 21165
Registriert: Di, 29. Nov 2005 16:58
Wohnort: Kreis Wesel
Hat sich bedankt: 206 Mal
Danksagung erhalten: 67 Mal

Re: Begin Sequence ... Recover ...

Beitrag von Manfred »

Code: Alles auswählen

bSaveError := ErrorBlock( {|e|BREAK(e)})
       DO WHILE ! (::nArea)->(Eof())                                            
          BEGIN SEQUENCE
                (oDbNeu:nArea)->(DbAppend())
                 AEval(::aTempArray, {|x,nI| Eval(::aTempArray[nI]) })
                 IF ++nZaehler % nModulo = 0 .OR. nZaehler < 100
                    oStatic3:SetCaption(Alltrim(Str(nZaehler)))
                 ENDIF
                 (::nArea)->(DbSkip())
          RECOVER USING oError
                 altd()         // der dient der Fehlersuche
                 oSysPara:oPruefen:fehlerbehandlung(oError,self)                       
           END SEQUENCE
          ErrorBlock(bSaveError)
       ENDDO
Gruß Manfred
Mitglied der XUG Osnabrück
Schatzmeister des Deutschsprachige Xbase-Entwickler e.V.
großer Fan des Xbaseentwicklerwiki https://wiki.xbaseentwickler.de/index.p ... Hauptseite
Doof kann man sein, man muß sich nur zu helfen wissen!!
Benutzeravatar
Tom
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 9345
Registriert: Do, 22. Sep 2005 23:11
Wohnort: Berlin
Hat sich bedankt: 100 Mal
Danksagung erhalten: 359 Mal
Kontaktdaten:

Re: Begin Sequence ... Recover ...

Beitrag von Tom »

Hallo, Jan.

Ein weiteres Beispiel wäre die Reparatur korrupter Tabellen. Leider geschieht das zuweilen ja - mittendrin steht plötzlich Garbage, oder die gute alte DBT hat die 2-Gigabyte-Grenze überschritten, weshalb es Lesefehler beim Zugriff auf die Tabelle gibt. Die Lösung besteht darin, den Zugriff auf die Felder (FieldGet()), der in solchen Situationen ja auch Fehler erzeugt, beim Umkopieren der Daten in die korrigierte Tabelle in eine Sequenz einzubetten - und einfach zum nächsten Feld zu springen. Dadurch erhält man ohne Laufzeitfehler eine instandgesetzte Tabelle, die man dann weiter inhaltlich prüfen kann.

Hier ein Beispiel zu einer "selbstgebauten" Filterbedingung:

Code: Alles auswählen

* "cAlias" enthält den Alias der Tabelle, in der gesucht werden soll, "cSuchbedingung" den Suchbegriff
LOCAL oError, bSaveError
bSaveError := ErrorBlock() // alten Error-Codeblock sichern
ErrorBlock( {|e| Break(e)} ) // neuen Error-Codeblock etablieren - Break im Fehlerfall (Sprung zum "Recover")
BEGIN SEQUENCE
DbSelectArea(cAlias)
LOCATE FOR &cSuchBedingung // geht auch eleganter
RECOVER USING oError
  MsgBox('Der Suchbegriff ist falsch!','Suche')
END SEQUENCE
ErrorBlock(bSaveError) // alten Error-Codeblock restaurieren
Alles zwischen "RECOVER" und "END SEQUENCE" wird nur angefasst, wenn es im Code zwischen "BEGIN SEQUENCE" und "RECOVER" einen Laufzeitfehler gab.
Herzlich,
Tom
Antworten