Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht Das deutsche QBasic- und FreeBASIC-Forum
Für euch erreichbar unter qb-forum.de, fb-forum.de und freebasic-forum.de!
 
FAQFAQ   SuchenSuchen   MitgliederlisteMitgliederliste   BenutzergruppenBenutzergruppen  RegistrierenRegistrieren
ProfilProfil   Einloggen, um private Nachrichten zu lesenEinloggen, um private Nachrichten zu lesen   LoginLogin
Zur Begleitseite des Forums / Chat / Impressum
Aktueller Forenpartner:

[Makro-Vorstellung] Fehlerbehandlung mit Try/Catch/Finally

 
Neues Thema eröffnen   Neue Antwort erstellen    Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht -> Windows-spezifische Fragen
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen  

Sind diese Makros nützlich?
Ja
100%
 100%  [ 3 ]
Nein
0%
 0%  [ 0 ]
Ich verstehe ihren Sinn nicht
0%
 0%  [ 0 ]
Stimmen insgesamt : 3

Autor Nachricht
Cherry



Anmeldungsdatum: 20.06.2007
Beiträge: 249

BeitragVerfasst am: 01.06.2009, 13:22    Titel: [Makro-Vorstellung] Fehlerbehandlung mit Try/Catch/Finally Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
Cherry



Anmeldungsdatum: 20.06.2007
Beiträge: 249

BeitragVerfasst am: 10.06.2009, 17:44    Titel: Antworten mit Zitat

Wundert mich, dass das keinen interessiert. Falls man dachte, es handle sich um eine Anfrage, hab ich mal den Titel geändert.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
nemored



Anmeldungsdatum: 22.02.2007
Beiträge: 4594
Wohnort: ~/

BeitragVerfasst am: 10.06.2009, 18:01    Titel: Antworten mit Zitat

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 happy )
_________________
Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
St_W



Anmeldungsdatum: 22.07.2007
Beiträge: 949
Wohnort: Austria

BeitragVerfasst am: 10.06.2009, 21:19    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
Cherry



Anmeldungsdatum: 20.06.2007
Beiträge: 249

BeitragVerfasst am: 10.06.2009, 22:38    Titel: Antworten mit Zitat

Hm, vllt sollte ich das noch erweitern, sodass es auch FreeBASIC-Fehler abfängt.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
Cherry



Anmeldungsdatum: 20.06.2007
Beiträge: 249

BeitragVerfasst am: 06.09.2010, 21:00    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
Beiträge der letzten Zeit anzeigen:   
Neues Thema eröffnen   Neue Antwort erstellen    Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht -> Windows-spezifische Fragen Alle Zeiten sind GMT + 1 Stunde
Seite 1 von 1

 
Gehe zu:  
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.

 Impressum :: Datenschutz