|
Das deutsche QBasic- und FreeBASIC-Forum Für euch erreichbar unter qb-forum.de, fb-forum.de und freebasic-forum.de!
|
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Sind diese Makros nützlich? |
Ja |
|
100% |
[ 3 ] |
Nein |
|
0% |
[ 0 ] |
Ich verstehe ihren Sinn nicht |
|
0% |
[ 0 ] |
|
Stimmen insgesamt : 3 |
|
Autor |
Nachricht |
Cherry
Anmeldungsdatum: 20.06.2007 Beiträge: 249
|
Verfasst am: 01.06.2009, 13:22 Titel: [Makro-Vorstellung] Fehlerbehandlung mit Try/Catch/Finally |
|
|
Hallo!
Ich habe einige Makros erstellt, mit denen man Try/Catch/Finally-Blöcke umsetzen kann. Leider muss man für jeden Try-Block einen Namen vergeben, da ich keine Möglichkeit gefunden habe, einen Zähler und einen Stack mit dem Preprozessor umzusetzen...
Falls es jemand nicht weiß: Try/Catch/Finally dient dazu, Fehler aufzufangen (entweder selbst erzeugte oder solche wie Access Violations (ungültige Speicherzugriffe) u.ä.). Wenn innerhalb eines Try-Blocks ein Fehler auftritt, wird zum entsprechenden Catch-Block gesprungen, oder, wenn kein passender existiert, zu dem des nächst höheren Try-Blocks. Beim Verlassen des Try-Blocks, egal ob ein Fehler auftrat oder nicht, wird der Finally-Code ausgeführt (z.B. zum Schließen von Dateien).
Try(name) : Startet einen Try-Block
Catch(name, exception codes) : Fängt einen oder meherere Fehler auf. Für "exception codes" können die Werte genauso wie bei "Case" von FreeBasic angegeben werden, allerdings muss statt einen Komma "__" (2x Unterstrich) verwendet werden.
Beispiele:
Code: | Catch(name, EXCEPTION_ACCESS_VIOLATION)
Catch(name, EXCEPTION_ACCESS_VIOLATION __ EXCEPTION_INT_DIVIDE_BY_ZERO)
Catch(name, Is <> EXCEPTION_INVALID_INSTRUCTION)
Catch(name, &hC0000000 To &hC000FFFF)
Catch(name, Else) ; fängt alle anderen Fehler auf |
Eine Liste der Windows-Fehler gibt es hier: http://xurl.sh/exceptioncodes
Finally(name) : Code ab dieser Stelle wird immer ausgeführt, auch wenn ein Fehler auftrat.
EndTry(name) : Beendet einen Try-Block
Leave(name) : Führt den Finally-Code aus. Leave kann z.B. aufgerufen werden, wenn mittels Return, Exit Sub o.ä. außerhalb des Try-Blocks gesprungen wird.
ExitTry(name) : Führt den Finally-Code aus und verlässt den Try-Block
Innerhalb des Catch-Codes können außerdem folgende Variablen genutzt werden:
ECode : gibt den Exception Code zurück
EAddress : gibt die Adresse, an der die Exception auftrat, zurück
ENumParams : gibt die Anzahl der Parameter zurück
EParamsPtr : Pointer auf das Parameter-Array
EParams(i) : Gibt den Parameter Nummer i zurück (beginnt bei 0). Bei ungültigen Nummern wird 0 zurückgegeben. Bei dem Fehler EXCEPTION_ACCESS_VIOLATION enthält EParams(1) die Adresse, auf die zugegriffen wurde, und EParams(0) gibt an, ob gelesen (0) oder geschrieben (1) wurde.
ERecord : Pointer auf den EXCEPTION_RECORD des Fehlers.
EContext: Pointer auf den CONTEXT des Fehlers.
Um selbst Exceptions zu erzeugen, können diese beiden Befehle genutzt werden:
Raise(exception code) : Wirft eine Exception ohne Parameter
RaiseParam(exception code, anzahl parameter, parameter) : Wirft eine Exception mit Parameter. Die Parameter (Integers) werden mit "__" getrennt.
Zu Beachten ist, dass Bit 28 immer auf 0 gesetzt wird, da es von Windows intern verwendet wird. Der Exception Code sollte auch folgende Regeln befolgen:
Bits 31-30 : 0=Erfolg, 1=Information, 2=Warnung, 3=Fehler
Bit 29 : 0=Microsoft, 1=Eigenes Programm
Bit 28 : Muss 0 sein
Bit 27-0 : Eigentlicher Code
Ein typischer Exception Code wäre z.B. &hE0000100 (Fehler, Eigenes Programm, Code &h100).
Beispiel:
Code: | #Include "windows.bi"
#Include "exceptions.bi"
Declare Sub Test1()
Declare Sub Test2()
Print "Bevor Test1()"
Test1()
Print "Nach Test1()"
Try(orange)
Print "in Try(orange)"
Print "Bevor Test2()"
Test2()
Catch(orange, &hE0012345 __ &hE0012346)
Print "Fehler E0012345 oder E0012346 aufgetreten! Gefangen von orange."
EndTry(orange)
Print "Nach Test2()"
Sleep
Sub Test1()
Try(apfel)
Print "in Try(apfel)"
Print "jetzt machen wir mal eine Access Violation!"
Print "Zugriff auf ungültigen Pointer!"
Dim x As Integer Ptr = 222
*x = 0
Print "Dies sollte nie erscheinen!"
Catch(apfel, EXCEPTION_INT_DIVIDE_BY_ZERO)
Print "Division durch 0! Dies sollte nie erscheinen!"
Catch(apfel, EXCEPTION_ACCESS_VIOLATION)
Print "Ohje, ein Fehler!"
Print "Code: " & Hex(ECode)
Print "an Adresse: " & Hex(EAddress)
Catch(apfel, Else)
Print "alle sonstigen Fehler würden hier aufgefangen werden"
Finally(apfel)
Print "Dieser Code wird immer ausgeführt, egal ob ein Fehler auftrat oder nicht."
Print "Hier könnte z.B. ""Close #1"" stehen, oder ""Delete blub""."
EndTry(apfel)
Print "Nun außerhalb von Try(apfel)!"
Do
Try(birne)
Print "in Try(birne)"
Print "jetzt verlassen wir den Try- und den Do-Block rundherum, mit ""Exit Do""."
Print "vorher führen wir noch ""Leave(birne)"" aus, so wird der Finally-Code auch ausgeführt."
Leave(birne): Exit Do
Print "Dies sollte nie erscheinen!"
Finally(birne)
Print "in Finally(birne)!"
EndTry(birne)
Loop
Print "Nun wieder außerhalb von Try(birne)!"
End Sub
Sub Test2()
Try(pfirsich)
Print "in Try(pfirsich)"
Print "nun werfen wir mal den Fehler E0012345"
Print "pfirsich fängt aber nur EXCEPTION_INT_DIVIDE_BY_ZERO!"
Print "also sollte es an orange weitergegeben werden"
Raise(&hE0012345)
Print "Dies sollte nie erscheinen!"
Catch(pfirsich, EXCEPTION_INT_DIVIDE_BY_ZERO)
Print "Division durch 0! Dies sollte nie erscheinen!"
Finally(pfirsich)
Print "in Finally(pfirsich)"
EndTry(pfirsich)
Print "Außerhalb von Try(pfirsich)"
End Sub
|
Hier nun die exceptions.bi:
Code: | #Ifndef EXCEPTION_RECORD
#Error Please include windows.bi first!
#EndIf
#Ifndef __FB_WIN32__
#Error Operating system not supported!
#EndIf
#Macro __trycheck(_name_)
#Ifndef __eh_##_name_##_try_set
#Error Undefined Try block name: _name_
#EndIf
#EndMacro
#Macro Try(_name_)
#Ifdef __eh_##_name_##_try_set2
#Error Duplicated definition: _name_
#EndIf
#Define __eh_##_name_##_try_set
#Define __eh_##_name_##_try_set2
Scope
Dim As Byte __eh_##_name_##_doret = 0
Dim As Any Ptr __eh_##_name_##_ehesp = 0
Dim As Byte __eh_##_name_##_ehandled = 0
Dim As Byte __eh_##_name_##_wasincatch = 0
Dim As EXCEPTION_RECORD Ptr __eh_##_name_##_erec = 0
Dim As Byte __eh_##_name_##_erecarr(SizeOf(EXCEPTION_RECORD))
Dim As Byte __eh_##_name_##_econarr(SizeOf(CONTEXT))
Asm
__eh_##_name_##_try:
pushad
mov esi, offset __eh_##_name_##_except
push esi
push dword ptr fs:[0]
mov dword ptr fs:[0], esp
End Asm
Scope
#EndMacro
#Macro __catch_codestart(_name_)
End Scope
#Define __eh_##_name_##_except_set
Asm
jmp __eh_##_name_##_finally_continue
__eh_##_name_##_except:
mov eax, [esp + 4]
mov eax, [eax + 4]
cmp eax, 2
jne __eh_##_name_##_nounwind
mov byte ptr [__eh_##_name_##_doret], 1
call __eh_##_name_##_finally
mov eax, 1
ret
__eh_##_name_##_nounwind:
mov eax, esp
mov esp, [esp + 8]
pop dword ptr fs:[0]
mov [esp], eax
add esp, 4
popad
mov eax, [esp - 36]
mov [__eh_##_name_##_ehesp], eax
mov edx, eax
End Asm
Scope
Dim As EXCEPTION_RECORD Ptr ERecord
Dim As CONTEXT Ptr EContext
#Define ECode (ERecord->ExceptionCode)
#Define EAddress (ERecord->ExceptionAddress)
#Define ENumParams (ERecord->NumberParameters)
#Define EParamsPtr CPtr(Integer Ptr, (@ERecord->ExceptionInformation(0)))
Asm
push edx
mov edx, [edx + 8]
mov [EContext], edx
pop edx
mov edx, [edx + 4]
mov [ERecord], edx
End Asm
#Define __ersize (SizeOf(EXCEPTION_RECORD) + ERecord->NumberParameters * 4 - 4)
#Define __erarrsize (UBound(__eh_##_name_##_erecarr) + 1)
CopyMemory(Cast(Any Ptr, @__eh_##_name_##_erecarr(0)), Cast(Any Ptr, ERecord), IIf(__ersize <= __erarrsize, __ersize, __erarrsize))
#Undef __erarrsize
#Undef __ersize
ERecord = Cast(EXCEPTION_RECORD Ptr, @__eh_##_name_##_erecarr(0))
__eh_##_name_##_erec = ERecord
CopyMemory(Cast(Any Ptr, @__eh_##_name_##_econarr(0)), Cast(Any Ptr, EContext), UBound(__eh_##_name_##_econarr) + 1)
EContext = Cast(CONTEXT Ptr, @__eh_##_name_##_econarr(0))
Select Case ECode
#EndMacro
#Define EParams(_i_) IIf(EParamsPtr <> 0 AndAlso _i_ < ENumParams, (EParamsPtr)[_i_], 0)
#Macro __catch_next(_name_, _ecode_)
#Define __ ,
Case _ecode_
#Undef __
Asm mov byte ptr [__eh_##_name_##_ehandled], 1
Asm mov byte ptr [__eh_##_name_##_wasincatch], 1
#EndMacro
#Macro Catch(_name_, _ecode_)
__trycheck(_name_)
#Ifdef __eh_##_name_##_finally_set
#Error Using Catch after Finally not allowed!
#EndIf
#Ifndef __eh_##_name_##_except_set
__catch_codestart(_name_)
#EndIf
__catch_next(_name_, _ecode_)
#EndMacro
#Macro Finally(_name_)
#Ifdef __eh_##_name_##_finally_set
#Error Multiple Finally statements not allowed!
#EndIf
__trycheck(_name_)
#Ifndef __eh_##_name_##_except_set
Catch(_name_, 0)
__eh_##_name_##_ehandled = 0
#EndIf
End Select
End Scope
#Define __eh_##_name_##_finally_set
Asm
mov byte ptr [__eh_##_name_##_doret], 0
__eh_##_name_##_finally:
End Asm
Scope
Select Case 0
Case 0
#EndMacro
#Macro Leave(_name_)
__trycheck(_name_)
Asm
mov byte ptr [__eh_##_name_##_doret], 1
call __eh_##_name_##_finally
End Asm
#EndMacro
#Macro EndTry(_name_)
__trycheck(_name_)
#Ifndef __eh_##_name_##_except_set
Catch(_name_, 0)
__eh_##_name_##_ehandled = 0
#EndIf
#Ifndef __eh_##_name_##_finally_set
Finally(_name_)
#EndIf
End Select
End Scope
Asm
cmp byte ptr [__eh_##_name_##_doret], 0
je __eh_##_name_##_noret
ret
__eh_##_name_##_noret:
cmp byte ptr [__eh_##_name_##_ehandled], 0
jne __eh_##_name_##_continue
mov esp, [__eh_##_name_##_ehesp]
End Asm
RaiseException(__eh_##_name_##_erec->ExceptionCode, __eh_##_name_##_erec->ExceptionFlags, __eh_##_name_##_erec->NumberParameters, @__eh_##_name_##_erec->ExceptionInformation(0))
Asm
int3
__eh_##_name_##_finally_continue:
mov byte ptr [__eh_##_name_##_doret], 1
call __eh_##_name_##_finally
pop dword ptr fs:[0]
add esp, 36
jmp __eh_##_name_##_end_try
__eh_##_name_##_continue:
cmp byte ptr [__eh_##_name_##_wasincatch], 1
je __eh_##_name_##_end_try
pop dword ptr fs:[0]
add esp, 36
__eh_##_name_##_end_try:
End Asm
End Scope
#Undef __eh_##_name_##_try_set
#EndMacro
#Macro ExitTry(_name_)
__trycheck(_name_)
Leave(_name_)
Asm jmp __eh_##_name_##_end_try
#EndMacro
#Macro RaiseParam(_ecode_, _nump_, _params_)
Scope
#Define __ ,
Dim __params(_nump_ - 1) As Integer = {_params_}
#Undef __
RaiseException(_ecode_, 0, _nump_, @__params(0))
End Scope
#EndMacro
#Macro Raise(_ecode_)
RaiseException(_ecode_, 0, 0, 0)
#EndMacro
|
Viel Spaß!
mfG Cherry
Zuletzt bearbeitet von Cherry am 02.01.2012, 21:44, insgesamt 2-mal bearbeitet |
|
Nach oben |
|
|
Cherry
Anmeldungsdatum: 20.06.2007 Beiträge: 249
|
Verfasst am: 10.06.2009, 17:44 Titel: |
|
|
Wundert mich, dass das keinen interessiert. Falls man dachte, es handle sich um eine Anfrage, hab ich mal den Titel geändert. |
|
Nach oben |
|
|
nemored
Anmeldungsdatum: 22.02.2007 Beiträge: 4594 Wohnort: ~/
|
Verfasst am: 10.06.2009, 18:01 Titel: |
|
|
Muss ganz ehrlich sagen, dass ich noch nie wirklich mit Try/Catch gearbeitet habe (vom Abtippen einiger Testcodes abgesehen) und es daher bisher auch (noch?) nicht vermisse.
(Abgesehen davon interessieren mich windowsspezifische Fragen sowieso nicht so sehr ) _________________ Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1. |
|
Nach oben |
|
|
St_W
Anmeldungsdatum: 22.07.2007 Beiträge: 949 Wohnort: Austria
|
Verfasst am: 10.06.2009, 21:19 Titel: |
|
|
Ich finde das Try-Catch-Finally einfach toll und hab in FB auch schon öfters eine ordentliche Fehlerbehandlung vermisst (das Programm läuft ja einfach weiter, wie wenn nichts gewesen wäre).
Ich habe jetzt deine Makros noch nicht ausprobiert, werde dies aber sicher in nächster Zeit einmal tun, wenn ich Zeit habe.
Ich hoffe, dass der Compiler selbst einmal eine solche Fehlerbehandlungsroutine bietet - ohne zusätzliche Makros. Funktionen, die der Compiler nativ unterstützt sind mir nämlich meist lieber, als so halbe Lösungen (sorry Cherry, nichts gegen dich und dein Werk - das weiß ich natürlich zu schätzen). _________________ Aktuelle FreeBasic Builds, Projekte, Code-Snippets unter http://users.freebasic-portal.de/stw/
http://www.mv-lacken.at Musikverein Lacken (MV Lacken) |
|
Nach oben |
|
|
Cherry
Anmeldungsdatum: 20.06.2007 Beiträge: 249
|
Verfasst am: 10.06.2009, 22:38 Titel: |
|
|
Hm, vllt sollte ich das noch erweitern, sodass es auch FreeBASIC-Fehler abfängt. |
|
Nach oben |
|
|
Cherry
Anmeldungsdatum: 20.06.2007 Beiträge: 249
|
Verfasst am: 06.09.2010, 21:00 Titel: |
|
|
Ich hab grade bemerkt, dass das "+ &hC000" bei "Dim As Byte __eh_##_name_##_erecarr(SizeOf(EXCEPTION_RECORD) + &hC000)" sinnlos ist, und nur leichter zu einem Stack Overflow bei verschachtelten Try-Blöcken führt. Ich habe das damals irrtümlich eingebaut, weil ich nicht mitbekommen habe, dass die maximale Anzahl an Fehlerparametern sowieso fix ist.
Daher hab ichs jetzt entfernt. |
|
Nach oben |
|
|
|
|
Du kannst keine Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum nicht antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen.
|
|