Fehler im Script abfangen/Eigene Fehlermeldung

  • Hallöchen,
    ich wurde nun schon mehrfach gebeten zu erklären, wie ich in meinem Programm GrooveLoad die Standart-AutoIt Fehlermeldung durch einen eigene ersetzt habe (kann man sich einfach angucken, indem man in GrooveLoad F10 drückt, dann stürzt es nach 3 Piepsern ab).

    Für erfahrenere AutoItler ist das Abfangen von Fehlern wohl nichts Neues, das hier richtet sich also mehr an die Anfanger.
    Aber was bringt das überhaupt? Der normale Nutzer, der noch nie das Wort AutoIt gehört hat, kann mit einer AutoIt Fehlermeldung absolut nichts anfangen und ärgert sich über den Absturz mit nutzloser Benachrichtigung. Eine eigene Fehlermeldung hingegen kann beispielsweise die Möglichkeit bieten, einen Fehlerbericht zu versenden oder das Programm neu zu starten.

    Erstmal: Für das Erstellen einer eignene Fehlermeldung braucht man 2 Scripte. Eines, was das Hauptprogramm beinhaltet und eines, was für das Abfangen und Anzeigen der Fehler zuständig ist. Ich habe nun mal 2 Beispielscripte erstellt, "Programm.au3" und "ErrorHandler.au3". Da die Scripte (hoffentlich) gut kommentiert sind, werde ich jetzt nicht auf jede Einzelheit, sondern nur auf den groben Ablauf eingehen.

    ErrorHandler.au3
    [autoit]

    #NoTrayIcon ; Kein unnötiges Tray Icon für den "ErrorHandler" anzeigen
    #include <EditConstants.au3> ; Wird für die GUI benötigt, kann bei anderer GUI evtl. entfernt werden.
    FileChangeDir (@ScriptDir)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    If @Compiled Then ; Liegt das Programm als kompilierte Version (als EXE) vor?
    $pfad = @ScriptDir & "\Programm.exe" ; Wenn JA, das Hauptprogramm "Programm.exe" mit dem Parameter "-Dihydrogenmonoxid-" starten.
    $iPID=Run("Programm.exe" & ' /ErrorStdOut -Dihydrogenmonoxid-',"",Default,6)
    Else
    $pfad = @ScriptDir & "\Programm.au3" ; Wenn NEIN, das Hauptscript "Programm.au3" mit dem Parameter "-Dihydrogenmonoxid-" über den AutoIt Interpreter starten.
    $iPID=Run(@AutoItExe & ' /ErrorStdOut "'&$pfad&'" -Dihydrogenmonoxid-',"",Default,6)
    EndIf
    ; Der Parameter /ErrorStdOut sorgt dafür, dass die Fehlermeldung nicht als MsgBox ausgegeben, sondern an den STDOUT Stream weitergegeben wird, den der "ErrorHandler" laufen überwacht.
    ; Hierzu aus der AutoIt Hilfe:
    ; The AutoIt3.exe interpreter, or the interpreter stub of any compiled Autoit script, can normally be used to run AutoIt scripts directly from the command line.
    ; In all cases the /ErrorStdOut switch allows the redirection of a fatal error to StdOut which can then be captured by an application such as the SciTE editor. This switch can be used with
    ; both the interpreter and a compiled script.

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $sErrorMsg = ""

    [/autoit] [autoit][/autoit] [autoit]

    ProcessWait($iPID) ; Warten, bis das Hauptprogramm auch wirklich läuft

    [/autoit] [autoit][/autoit] [autoit]

    While 1 ; Laufend überprüfen, ob das Hauptprogramm etwas in den STDOUT Stream schreibt und diese Daten sammeln.
    $sErrorMsg&=StdoutRead($iPID)
    If @error Then ExitLoop ; Wenn der Prozess des Hauptprogramms nicht mehr existiert (da abgestürzt oder beendet) die Schleife verlassen.
    Sleep(10)
    WEnd
    If $sErrorMsg = "" Then Exit ; Wenn keine Daten gesammelt wurden (es wurde also keine Fehlermeldung ausgegeben und das Programm normal beendet), ebenfalls den "ErrorHandler" beenden.
    ; Wurde jedoch eine Fehlermeldung ausgegeben, wird diese im Folgenden in einer GUI ausgegeben.

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Im Folgenden wird die GUI angezeigt. Die GUI kann natürlich beliebig angepasst werden, hier sind der Kreativität keine Grenzen gesetzt.
    ; Wichtig ist jedoch: Alles sollte 100 prozentig fehlerfrei programmiert sein! Ein Absturz des "ErrorHandlers" wird nämlich nicht abgefangen und wäre mehr als peinlich ;)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Dim $text[5]

    [/autoit] [autoit][/autoit] [autoit]

    $text[0]="Das Programm ist nach einem schweren Fehler abgestürzt:"
    $text[1]="Fehlerbericht senden"
    $text[2]="Neu starten"
    $text[3]="Beenden"
    $text[4]="Bitte beschreibe hier, was du vor dem Programmabsturz getan hast."

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $GUI = GUICreate("Fehler", 419, 192,-1,-1,-1)
    GUISetBkColor(0xFFFFFF)
    $Label1 = GUICtrlCreateLabel("Houston, we’ve had a problem.", 8, 3, 364, 25)
    GUICtrlSetFont(-1, 12, 400, 0, "Arial Black")
    GUICtrlSetColor(-1, 0xF77F00)
    GUICtrlCreateLabel($text[0], 8, 28, 311, 17)
    $send = GUICtrlCreateButton($text[1], 8, 158, 131, 25)
    $restart = GUICtrlCreateButton($text[2], 144, 158, 131, 25)
    $exit = GUICtrlCreateButton($text[3], 280, 158, 131, 25)
    GUICtrlCreateEdit("", 8, 50, 401, 100,BitOR ($ES_READONLY,$GUI_SS_DEFAULT_EDIT))
    $sErrorMsg = $sErrorMsg&@CRLF & @OSVersion & " " & @OSServicePack & " "&@OSArch ; Fehlermldung um Daten zum Betriebssystem erweitern
    GUICtrlSetData(-1, $sErrorMsg)
    GUISetState(@SW_SHOW)
    WinSetOnTop ($GUI,"",1) ; GUI in den Vordergrund

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; X-Symbol in der GUI deaktivieren
    Dim Const $SC_CLOSE = 0xF060
    $dSysMenu = DllCall("User32.dll", "hwnd", "GetSystemMenu", "hwnd", $GUI, "int", 0)
    $hSysMenu = $dSysMenu[0]
    DllCall("User32.dll", "int", "RemoveMenu", "hwnd", $hSysMenu, "int", $SC_CLOSE, "int", 0)
    DllCall("User32.dll", "int", "DrawMenuBar", "hwnd", $GUI)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    DllCall ("user32.dll", "int", "MessageBeep", "int", 0x00000010) ; Ton ausgeben

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    While 1 ; Eingaben des Nutzers in der GUI abfragen
    $nMsg = GUIGetMsg()
    Switch $nMsg
    Case $exit
    Exit
    Case $send
    MsgBox (0,"","Programmiere mich :)",0,$GUI)
    Case $restart
    ShellExecute (@AutoItExe,'"'&@ScriptFullPath&'"')
    Exit

    [/autoit] [autoit][/autoit] [autoit]

    EndSwitch
    WEnd

    [/autoit]
    Programm.au3
    [autoit]

    If Not StringInStr($cmdlineraw, "-Dihydrogenmonoxid-") Then ; Wurde das Programm mit dem Parameter "-Dihydrogenmonoxid-" gestartet? Wenn nein, wird im Folgenden der "ErrorHandler" gestartet,
    ; das Script, das die Fehler abfängt, wenn ja, läuft er bereits und das Programm wird normal gestartet.
    If @Compiled Then ; Liegt das Programm in kompilierter Form (als EXE) vor?
    ShellExecute ("ErrorHandler.exe") ; Wenn JA, dann den "ErrorHandler" als EXE starten.
    Else
    ShellExecute(@AutoItExe, '"' & @ScriptDir & '\ErrorHandler.au3"') ; Wenn NEIN, den "ErrorHandler" als AU3 Datei starten. Der Start der AU3 Datei erfolgt über den AutoIt Interpreter AutoIt.exe,
    ; der nach einer AutoIt installation auf dem Computer vorhanden ist und dessen Pfad mit dem Macro @AutoItExe abgefragt werden kann.
    EndIf
    Exit ; Da das Programm über den "ErrorHandler" neu gestartet wird bzw. wurde, wird diese Instanz nun beendet.
    EndIf

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Es folgt das Programm
    Dim $var[3] ; Diese Variable ist später für den Absturz verantwortlich

    [/autoit] [autoit][/autoit] [autoit]

    GUICreate ("Super tolles Programm",500,500)
    GUICtrlCreateEdit ("Hier kannst du was reinschreiben",5,5,490,450)
    $Absturz = GUICtrlCreateButton ("Abstürzen",5,460)
    ;ConsoleWrite ("bäääm") ; !!! Wichtig: Im Programm muss auf die Verwendung von ConsoleWrite verzichtet werden, da sich sonst nach Beenden des Programms der "ErrorHandler" meldet und sämtliche
    ; über die Konsole ausgegebenen Texte als Fehlermeldung ausgibt. Zum Testen einfach den Strichpunkt vor ConsoleWrite entfernen.
    GUISetState ()

    [/autoit] [autoit][/autoit] [autoit]

    While 1
    $msg = GUIGetMsg ()
    If $msg = -3 Then Exit
    If $msg = $Absturz Then $var[5] = "blub" ; Hier wird ein Fehler provoziert, das Array $var hat nämlich nur einen Umfang von 3, allerdings wird dem 5. Index ein Wert zugewiesen.
    WEnd

    [/autoit]


    Ablauf einer Programmausführung:
    1. Programm.au3 wird gestartet und prüft, ob der Parameter "-Dihydrogenmonoxid-" mitgegeben wurde. Da das Programm einfach per Doppelklick gestartet wurde, ist dies natürlich nicht der Fall

    2. Programm.au3 startet ErrorHandler.au3 und beendet sich darufhin selbst.

    3. ErrorHandler.au3 startet Programm.au3 mit dem Parameter "-Dihydrogenmonoxid-" und überwacht fortan im Hintergrund den STDOUT Stream, wo alle Fehlermeldungen hineingeschrieben werden. Klingt kompliziert, ist es nicht. Im Grunde lauscht ErrorHandler.au3 einfach an einer Schnittstelle, die für die Kommunikation zwischen Programmen da ist. Den STDOUT Stream kann man auch manuell beschreiben, indem man den Befehl "ConsoleWrite" verwendet, der etwas in die SciTE Konsole hineinschreibt. Denn auch SciTE hört den STDOUT Stream ab und zeigt alles an, was das Programm auf diesem Weg von sich gibt, also ausgaben über ConsoleWrite oder auch Fehlermeldungen. Das ConsoleWrite-Befehle und Fehlermeldungen den gleichen Weg zur Ausgabe nutzen, hat allerdings einen Nachteil: So ohne weites sind diese nicht voneinander zu unterscheiden (lässt sich natürlich über String-Vergleiche usw. trotzdem machen, aber das führt hier zu weit), deshalb darf in Programm.au3 der Befehl ConsoleWrite NICHT verwendet werden (siehe auch Programm.au3 Zeile 20 f.)!

    4. Programm.au3 wird irgendwann wieder beendet, sei es gewollt oder durch einen Absturz.

    5. ErrorHandler.au3 merkt das und schaut, ob sich das Programm mit einer Ausgabe im STDOUT Stream verabschiedet hat. Wenn nein, wertet er dies als geplantes Beenden und beendet sich selbst auch, wenn ja als Absturz und zeigt eine schicke Fehlermeldung an, die dem Nutzer z.B. die Option bietet, einen Fehlerbericht zu senden oder das Programm neu zu starten.

    Anmerkungen:
    Schritt 1 und 2 können übersprungen werden, wenn gleich ErrorHandler.au3 gestartet wird. Das Ganze funktioniert auch, wenn die 2 Scripte in komplierter Form vorliegen. Der Parameter "-Dihydrogenmonoxid-" kann natürlich verändert werden, das war nur das erstbeste Wort, was mir eingefallen ist :D :rolleyes:
    Das Ganze hier ist ein Beispiel zum Fehler abfangen und ich erhebe nicht den Anspruch, die einzig wahre Lösung gefunden zu haben :P


    Bevor ihr euch jetzt aber im Abfangen von Fehlern verkünstelt, solltet ihr eher versuchen, euer Programm fehlerfrei zu programmieren. Das nützt dem Nutzer am Ende nämlich am Meisten ;).
    Und wenn's Fragen gibt, dann fragt.


    Viele Grüße
    Cheater Dieter

  • Korrigiere mich wenn ich falsch liege (mit Objekten habe ich wenig am Hut), aber damit kann ich doch nur Fehler im Umgang mit Objekten auffangen. Aber Fehler (z.B. mit Variablen/Arrays), die auch zum Absturz des Programms führen doch nicht, ober habe ich da was übersehen?

  • Zitat

    Der normale Nutzer, der noch nie das Wort AutoIt gehört hat, kann mit einer AutoIt Fehlermeldung absolut nichts anfangen und ärgert sich über den Absturz mit nutzloser Benachrichtigung.

    made my day :rofl:
    Da wohl die meisten computeraffinen Menschen in ihrem Leben maximal eine Handvoll HILFREICHER Fehlermeldungen gesehen haben (die Krönung war bisher ein animiertes Fenster eines Bekannten mit dem Wahnsinns-Fehlermeldungstext:"Es ist ein Fehler aufgetreten!") , stellt sich nicht die Frage nach AutoIt, sondern nach Möglichkeiten, JEDER benutzbaren Programmiersprache HILFREICHE Fehlermeldungen abringen zu können.
    Wo ist bspw. eine ERROR-API, welche mir im Klartext zeigt, was schief gelaufen ist? Natürlich kann ich per GetLastError() den letzten Fehler abfragen, aber was bringt mir das als Entwickler, wenn ich weiß, dass der User mir am Telefon vorliest:"The specified attributes are invalid, or incompatible with the attributes for the group as a whole." klasse! Nur noch zu toppen von:"Incorrect size argument." rofl, jetzt kann ich mich durch tausende Zeilen Source wühlen um zu raten, wo dieser Fehler genau aufgetreten sein KÖNNTE!

    Ich selbst verwende, wie schon in einigen anderen Threads beschrieben, ein System, indem ich in der Fehlermeldung erstens die Funktion, die Unterfunktion und den wahrscheinlichen Grund für den Fehler beschreibe. So kann ich als "Entwickler" Fehler im Source auf +-20 Zeilen genau lokalisieren....
    Das ist bei Software, welche im professionellen Umfeld verwendet wird, für mich ein Qualitätsmerkmal!

    Der folgende Auszug aus dem MSDN spiegelt die Hilflosigkeit wieder, auftretende Fehler bzw. deren Meldungen, NICHT dem Programmierer aufs Auge drücken zu MÜSSEN! Errorhandling gehört in richtige Software genau wie die Beschreibung und Lokalisierung und Dokumentation. Also eine Arbeit, von der niedrig geschätzte 90% der Programmierer das letzte Mal im Studium gehört haben. Glücklicherweise hat die Versager- und Nietenlobby diesen "Programmierern" gerichtlich bestätigen lassen, dass es keine "fehlerfreien" Systeme aka Soft+ Hardware geben kann, weil ja die anderen Flitzpiepen auch ihre Finger drin haben^^. Wo würde es denn auch hinführen, wenn denjenigen, die regelmäßig systembedingt Sch*** bauen, der Job gekündigt wird, wie in jedem anderen produzierenden Unternehmen auch?!

    Zitat von MSDN

    The System Error Codes are very broad. Each one can occur in one of many hundreds of locations in the system. Consequently the descriptions of these codes cannot be very specific. Use of these codes requires some amount of investigation and analysis. You need to note both the programmatic and the run-time context in which these errors occur. Because these codes are defined in WinError.h for anyone to use, sometimes the codes are returned by non-system software. Sometimes the code is returned by a function deep in the stack and far removed from your code that is handling the error.

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    Einmal editiert, zuletzt von Andy (31. Mai 2014 um 19:53)