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
#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)
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.
$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.
; 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
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."
$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
; 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)
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
EndSwitch
WEnd
Programm.au3
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
; Es folgt das Programm
Dim $var[3] ; Diese Variable ist später für den Absturz verantwortlich
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 ()
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
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
Das Ganze hier ist ein Beispiel zum Fehler abfangen und ich erhebe nicht den Anspruch, die einzig wahre Lösung gefunden zu haben
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