Try/Catch Commands, supports Windows/Linux 32/64 Bit

Share your advanced PureBasic knowledge/code with the community.
User avatar
RocketRider
User
User
Posts: 87
Joined: Tue Aug 19, 2008 11:01 am

Try/Catch Commands, supports Windows/Linux 32/64 Bit

Post by RocketRider »

Hello

I have writen a few Try/Catch Commands.
Which supports Windows/Linux 32/64 Bit.

Code: Select all

;****************************
;***  Try/Catch Commands  ***
;****************************
;*** (c) 2009 RocketRider ***
;***    RocketRider.eu    ***
;****************************
;***    PureBasic 4.31    ***
;****************************
;+ Supports Windows/Linux 32/64 Bit
;+ Does not support multi threated applications at this time

DisableDebugger
Global __TRYCATCH_pOnErrorAddress.i
Global __TRYCATCH_pOldOnErrorAddress.i
Global __TRYCATCH_pTryCatchStackPointer.i
Global __TRYCATCH_pOldTryCatchStackPointer.i

__TRYCATCH_pOnErrorAddress = ?__TRYCATCH_DefaultExceptionHandler
OnErrorGoto(?__TRYCATCH_DefaultExceptionHandler)
Goto __TRYCATCH_EndDefaultExceptionHandler
__TRYCATCH_DefaultExceptionHandler:
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  MessageRequester("Unhandled Exception!", ErrorMessage(), #MB_ICONERROR)
CompilerElse
  MessageRequester("Unhandled Exception!", ErrorMessage())
CompilerEndIf
End
__TRYCATCH_EndDefaultExceptionHandler:

Macro Try(Name)
  __TRYCATCH_pOldOnErrorAddress = __TRYCATCH_pOnErrorAddress
  __TRYCATCH_pOldTryCatchStackPointer = __TRYCATCH_pTryCatchStackPointer
  __TRYCATCH_pOnErrorAddress = ?__TRYCATCH_#Name
  
  CompilerIf #PB_Compiler_Processor =#PB_Processor_x86
    !MOV dword[v___TRYCATCH_pTryCatchStackPointer],ESP
  CompilerElse
    CompilerIf #PB_Compiler_Processor =#PB_Processor_x64
      !MOV qword[v___TRYCATCH_pTryCatchStackPointer],RSP
    CompilerElse
      CompilerError "Processor not supported by try/catch handler"
    CompilerEndIf
  CompilerEndIf
  
  OnErrorGoto(?__TRYCATCH_#Name)
EndMacro
Macro Catch(Name)
  Goto __TRYCATCH_End#Name
  __TRYCATCH_#Name:
  CompilerIf #PB_Compiler_Processor =#PB_Processor_x86
    !MOV ESP,dword[v___TRYCATCH_pTryCatchStackPointer]
  CompilerElse
    CompilerIf #PB_Compiler_Processor =#PB_Processor_x64
      !MOV RSP,qword[v___TRYCATCH_pTryCatchStackPointer]
    CompilerElse
      CompilerError "Processor not supported by try/catch handler"
    CompilerEndIf
  CompilerEndIf
  __TRYCATCH_pTryCatchStackPointer = __TRYCATCH_pOldTryCatchStackPointer
  __TRYCATCH_pOnErrorAddress = __TRYCATCH_pOldOnErrorAddress
  OnErrorGoto(__TRYCATCH_pOnErrorAddress)
EndMacro
Macro EndCatch(Name)
  __TRYCATCH_End#Name:
  __TRYCATCH_pTryCatchStackPointer = __TRYCATCH_pOldTryCatchStackPointer
  If __TRYCATCH_pOnErrorAddress <> __TRYCATCH_pOldOnErrorAddress
    __TRYCATCH_pOnErrorAddress = __TRYCATCH_pOldOnErrorAddress
    OnErrorGoto(__TRYCATCH_pOnErrorAddress)
  EndIf
EndMacro



;Test exception handling
Try(INTERRUPT_TEST)  ; Use Try to begin the exeption handling. INTERRUPT_TEST is the name of the try/catch section. It can be anything, but must be unique. 
  ;Programcode which causes an exception
  !int 21h

Catch(INTERRUPT_TEST)  ; continues execution after Catch(...) if an exception occured, or jumps directly to EndCatch(...) if no exception occured.
  ;This part will be only called if an exception was raised.
  MessageRequester("Exception found!", ErrorMessage())
EndCatch(INTERRUPT_TEST) ; end try/catch section
;The stack will be corrected and the program can be continued.


; Now a second test...
Procedure Test()
  bResult = #True
  Try(POKEL_TEST)
    ;Cause an exception
    PokeL(0,0)
  
  Catch(POKEL_TEST)
    ;This part will be only called if an exception was raised.
    MessageRequester("Exception found in Procedure Test()!", ErrorMessage())
    ;Do not use ProcedureReturn into a Catch...EndCatch section, because exeption handle would not work correct, if nested try-blocks are used (EndCatch would be not called in this case).
    ;Instead st return value
    bResult = #False
  EndCatch(POKEL_TEST)
  ProcedureReturn bResult
EndProcedure

MessageRequester("Result", Str(Test()))

Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

Doesn't work with threads:

Code: Select all

Procedure CauseException(Null)
  !int 21h
  Repeat
    Delay(1)
  ForEver
EndProcedure

Try(INTERRUPT_TEST)

ThreadID=CreateThread(@CauseException(),0)
WaitThread(ThreadID)

Catch(INTERRUPT_TEST)
  MessageRequester("Exception found!",ErrorMessage())
EndCatch(INTERRUPT_TEST)
It also doesn't scale because your dynamic catch labels need to be unique:

Code: Select all

Try(INTERRUPT_TEST)
  !int 21h
Catch(INTERRUPT_TEST)
  MessageRequester("Exception found!",ErrorMessage())
EndCatch(INTERRUPT_TEST)

Try(INTERRUPT_TEST)
  !int 21h
Catch(INTERRUPT_TEST)
  MessageRequester("Exception found!",ErrorMessage())
EndCatch(INTERRUPT_TEST)
freak
PureBasic Team
PureBasic Team
Posts: 5944
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

Note that this will easily leak memory if you exit a procedure via the error handler because you just reset the stack but don't free any local resources.
quidquid Latine dictum sit altum videtur
User avatar
RocketRider
User
User
Posts: 87
Joined: Tue Aug 19, 2008 11:01 am

Post by RocketRider »

@freak:
Can you show me an example?
freak
PureBasic Team
PureBasic Team
Posts: 5944
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

Code: Select all

Procedure Something()
  a$ = "The quick brown fox jumped over the lazy dog"
  !int 21h
EndProcedure

Try(INTERRUPT_TEST)
  Something()
Catch(INTERRUPT_TEST)
  ; The memory used by a$ is now leaked...
EndCatch(INTERRUPT_TEST)
quidquid Latine dictum sit altum videtur
User avatar
RocketRider
User
User
Posts: 87
Joined: Tue Aug 19, 2008 11:01 am

Post by RocketRider »

How could we correct the code to correct the problem?


If the Try/Catch is in the Procedure it is no problem: :wink:

Code: Select all

Procedure Something() 
Try(INTERRUPT_TEST) 
  a$ = "The quick brown fox jumped over the lazy dog" 
  !int 21h 
Catch(INTERRUPT_TEST) 
 MessageRequester("Exception","")
EndCatch(INTERRUPT_TEST)
; The memory used by a$ will be freed correctly now...   
EndProcedure 


Something() 
freak
PureBasic Team
PureBasic Team
Posts: 5944
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

> How could we correct the code to correct the problem?

You can't. Stack unwinding for such exception handling has to be implemented by the compiler, it cannot be done through a few macros.


> If the Try/Catch is in the Procedure it is no problem:

Sure, but how useful is the whole thing if you cannot even call procedures inside the try-block ?
quidquid Latine dictum sit altum videtur
Post Reply