Table of Contents

11.5.0.1 Error handling 2

Error management in the Gambas programming language also includes the topics of recognising errors, catching errors and handling errors. For this purpose, Gambas provides the instructions TRY, FINALLY, CATCH, ERROR, DEBUG as well as the error class Error, a global, read-only Boolean variable Error and error numbers as well as error descriptions, which are described in this chapter.

11.5.0.1.1 Error Class Error (gb)

The Error class has these static, read-only properties:

PropertyData typeDescription
Backtrace String[]Returns a backtrace of the stack state of the function call when the last error occurred. The first entry of the string array is the lowest function call. If no debugging information was available or if no error occurred, this property returns NULL.
ClassClassReturns the name of the class in which the last error occurred.
CodeIntegerReturns the last error number.
TextStringReturns the last error message - corresponding to the associated error code in English.
WhereStringReturns a string describing the position of the last error in the source code.

Table 11.5.0.1.1 : Properties of the Error Class

Instead of the Error.Backtrace property of the Error class, you can also use the static property System.Backtrace (gb) with Static Property Read Backtrace As String[ ]. In both cases, a string array is returned to you that you can read out and display.

The Error class Error has only three methods:

MethodDescription
Clear()Sets the error code to 0 and the error message to NULL. You can use this method if you handle the error within a procedure and then set the properties Error.Code to 0 and Error.Text to NULL.
Propagate()The method ensures that the error is triggered again.
Raise( argMessage As String )Raises an error defined by the user. The error message in argMessage can be freely defined in the source code.

Table 11.5.0.1.2 : Methods of the error class

11.5.0.1.2 Statement DEBUG

DEBUG Expression [ { ; | ;; | , } Expression ... ] [ { ; | ;; | , }  ]
FMain.GetStart.69: /tmp/gambas.1000/13839/New.tmp/new.md.

The following example was about checking various paths. The instruction DEBUG - switched on by the boolean variable bDebug - was preferred to an instruction PRINT, because the line numbers in the source code were also of interest:

  Public bDebug As Boolean
 
  bDebug = True
 
  sBasicConfigDir = Desktop.ConfigDir &/ sVendor &/ Lower(sAppName)
 
  If bDebug Then Debug sBasicConfigDir   
 
  If Not Exist(sBasicConfigDir) Then Shell.MkDir(sBasicConfigDir)
  hSettings = New Settings(sBasicConfigDir &/ File.SetExt(Lower(sAppName), "conf")) 
 
  If bDebug Then Debug sBasicConfigDir &/ File.SetExt(Lower(sAppName), "conf")
  ...
  sCurrentMDFilePath = sTempDir &/ "new.md"
 
  If bDebug Then Debug sCurrentMDFilePath

Outputs in the console of the IDE:

FMain._new.49: /home/hans/.config/gambasbook/mdeditor
FMain._new.52: /home/hans/.config/gambasbook/mdeditor/mdeditor.conf
...
FMain.GetStart.69: /tmp/gambas.1000/13839/New.tmp/new.md

===== 11.5.0.1.3 Statement ERROR =====<code_b_3>

The statement prints a list of expressions on the standard error output - just like the PRINT statement. If you were writing a command-line program, this statement would be the non-graphical equivalent of Message.Error().

If in the IDE, in the Debug menu, you activate the entries 'Use Terminal Emulator' and “Redirect Standard Error Output” both with ✔, then DEBUG and ERROR output will be displayed in the IDE console and PRINT output in the terminal. However, if 'Use terminal emulator' is active and redirection is disabled, then DEBUG, ERROR and PRINT output will be displayed in the terminal:

Figure 11.5.0.1.1: DEBUG, ERROR and PRINT outputs in the (IDE) terminal.

The standard output can be redirected by the ERROR TO statement ( ▷ chapter 6.2.0.14 ERROR TO DEFAULT). A redirection is simply implemented:

DIM aFile As File

  aFile = OPEN User.Home &/ "error_ping.log" FOR CREATE ' or APPEND

  OUTPUT TO aFile  
  ...
    Error ...
  ...
  CLOSE aFile

This source code snippet redirects the output of the error statement to the specified file.

11.5.0.1.4 Global Variable Error

The boolean variable Error - global and read-only - returns the value True if an error has occurred. Use it directly after a Try statement to know whether the statement specified there failed or not. To get more information about the error, use the (error) class Error (gb) with success.

Example: Deleting a file

Public Sub btnKillFile_Click()
 
  Dim sMessage, sFilePath As String
 
  sFilePath = User.Home &/ "Temp" &/ "tmp.text"
 
  Try Kill sFilePath
  If Error Then Print "Error! The file `" & File.Name(sFilePath) & "` cannot be deleted!"
  If Error Then Error Subst("&1 '&2' &3", ("Error! The file"), File.Name(sFilePath), ("cannot be deleted!"))
  If Error Then Print Error.Class.Name;; Error.Code;; Error.Text;; Error.Where
  If Error Then Error "Error³!";; Error.Text
  If Error Then Error Error
  If Error Then Debug Error.Text
'--------------------------------------------------------------------------------------------------------
  If Error Then 
     sMessage = "<b><font size='+1', color='DarkRed'>"
     sMessage &= ("Error")
     sMessage &= "</b></font><hr>"
     sMessage &= "The file '" & File.Name(sFilePath) & "' cannot be deleted.<br>"
     sMessage &= Error.Text & "!<br>"
     sMessage &= ("The error was raised in the source code line") & " " & Split(Error.Where, ".")[2] & "."
     Message.Error(sMessage)
  Endif
'--------------------------------------------------------------------------------------------------------
  Error Error
End

The following is the output in the console of the IDE:

Error! The file `tmp.text` cannot be deleted!
Error! The file 'tmp.text' cannot be deleted!
FMain 45 File or directory does not exist FMain.btnKillFile_Click.10
Error³! File or directory does not exist
True
FMain.btnKillFile_Click.16: File or directory does not exist
True

BILD 2

Figure 11.5.0.1.2: Error message

Comment

If Error Then Error Error
' Gambas class file
 
Property Read Basis As Integer
Private $iBasis As Integer
 
Private Function Basis_Read() As Integer
  If FSetBasisDialog.ShowModal() = 1 Then 
     Return $iBasis
  Else
     Return 0
  Endif
End
 
Public Sub Form_Open()
  FSetBasisDialog.Resizable = False
End
 
Public Sub txbBasis_Activate()
  SetBasis()  
End
 
Public Sub btnOK_Click()
  SetBasis()
End
 
Private Function CheckInput(argInput As String) As Integer
 
  Dim iValue As Integer
 
  If Not argInput Then Error.Raise(("Die Eingabebox ist leer!"))
  If Not IsInteger(argInput) Then 
     Error.Raise(("Der Text kann <b>nicht</b> in eine Integer-Zahl konvertiert werden!"))
  EndIf
 
  iValue = Val(argInput)
 
  If iValue < 2 Then Error.Raise(("Die Basis ist kleiner als 2!"))
  If iValue > 32 Then Error.Raise(("Die Basis ist größer als 32!"))
 
  Return iValue
 
End
 
Private Sub SetBasis()
 
  $iBasis = CheckInput(txbBasis.Text)
  FSetBasisDialog.Close(1)  
 
  Catch  
    Message.Error(Error.Text)
    Error.Clear()
    txbBasis.Clear()
    txbBasis.SetFocus()
 
End

Comment

11.5.0.1.6 Example of the use of the Error.Clear() and Error.Propagate() methods

Preliminary note: In the source code of Gambas 3.12.2 you will not find a line with the method Error.Clear() and only 3 times the method Error.Propagate() is used. That says a lot about the importance of these two methods!

When an error is raised, the Gambas interpreter stops the execution of the current frame and looks for error handlers such as try or catch. If it cannot find them, the interpreter goes up the call stack and looks for error handlers there one by one until the global level is reached. There, as a last resort, the interpreter looks for the method `Static Public Sub Application_Error()` in the Start class. If the error could not be corrected, the interpreter issues an error message and terminates the programme.

To observe the automatic unwinding of the stack, look at the following module source code and the resulting output:

' Gambas module file
 
Public Sub Main()
    f()
  End
 
  Public Sub f()
    Try g()
    If Error Then 
       Print "\nError-Backtrace:\n----------------"
       Print Error.Backtrace.Join("\n")
    Endif
  End
 
  Public Sub g()
    h(4)
    Print "Error from h(arg) will jump over this. Jumps to FINALLY"
 
    Finally
      Print "\nPassing through g()"
    ' Error.Clear()
      Error.Propagate() ' Apparently handles the error by triggering it again.
  End
 
  Public Sub h(x As Integer)    
    Print "x = "; CStr(x); " | f(x) = "; 1 / x
    h(x - 1) ' Recursion - but without termination condition
    Print "Execution of h stops for x = 0 before we're here!"
  End

Outputs in the IDE console:

x = 4 | f(x) = 0,25
x = 3 | f(x) = 0,33333333333333
x = 2 | f(x) = 0,5
x = 1 | f(x) = 1

Passing through g()

Error-Backtrace:
----------------
Main.h.27
Main.h.28
Main.h.28
Main.h.28
Main.h.28
Main.g.16
Main.f.8
Main.Main.4

You could say that the Try and Catch statements take the interpreter out of the exceptional state. Unwinding of the batch is stopped and normal execution resumes. But if you have several “If Error Then” constructs in the source code, then you must use the Error.Clear() method after handling an error to avoid handling it again! For comparison, comment out the line calling the Error.Clear() method and then look at the output again!

11.5.0.1.7 Error numbers and error messages

Within an error handler, you can also target the displayed error numbers, which you can read via the Error.Code property of the Error (gb) class. For a complete list, see http://gambaswiki.org/wiki/error. Use the error numbers to generate meaningful error messages in German, because behind each error number is a short description of the triggered error in English, as the following examples show as an example:

(43)  Access forbidden
(44)  File name is too long
(45)  File or directory does not exist
(46)  File is a directory
(47)  Read error
(48)  Write error
Cannot find dynamic library (60)
Cannot find symbol in dynamic library (61)
Cannot load class (2)
Cannot load component (27)
Cannot open file (35)

The following source code excerpt sets an error message in German in the Select…Case control structure in the Catch block for selected error numbers. The error number is read from the Error.Code property:

Public Sub btnSaveFile_Click() 
 ' -> Saves the content of TextArea1 in the file to which the path in TextBox1 leads.
   Dim hFile As Stream
   Dim iCount As Integer
   Dim sErrorText As String
 
   hFile = Open TextBox1.Text For Write
   ...
     Write #hFile, bWrite, 1    
   ...  
 ' -> Close the stream in any case
   Finaly
     Close #hFile
   Catch
   Select Case Error.Code
     Case 43
       sErrorText = ("You do not have the rights to save this file.")
     Case 44
       sErrorText = ("The file name is too long.")
     Case 45
       sErrorText = ("The file or folder does not exist.")
     Case 46
       sErrorText = ("The path leads to a folder.")
     Case 48
       sErrorText = ("Error when writing to the file.")
     Case Else
       sErrorText = ""
   End Select  
 ' -> Output information about the triggered error
   Message.Error("Error-Code: " & Error.Code & "\n" & "bei " & Error.Where & "\n" & sErrorText)
 
End

Download