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:
| Property | Data type | Description |
|---|---|---|
| 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. |
| Class | Class | Returns the name of the class in which the last error occurred. |
| Code | Integer | Returns the last error number. |
| Text | String | Returns the last error message - corresponding to the associated error code in English. |
| Where | String | Returns 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:
| Method | Description |
|---|---|
| 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 ... ] [ { ; | ;; | , } ]
- The instruction prints a list of expressions on the standard error output. But only if the programme was compiled with debugging information. You can change the setting in the “Create executable” dialogue. The default setting is “Keep debugging information in the executable file”.
- The position of the DEBUG statement in the source code is prepended to everything.
- The expressions are converted into strings with the Str$ function, which takes into account the current location parameters such as GUI language, number, currency, date and time formats, character set and keyboard layout (locale)!
- If there is no semicolon and no comma after the last expression, a newline character is output after the last expression.
- If the semicolon is doubled, a space is output between each expression.
- If a comma is used instead of a semicolon, a tab character (ASCII code 9, gb.Tab) is output to separate each expression.
- The list of expressions is preceded by the name of the class, a full stop, the name of the procedure, a colon and the line number such as:
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
Figure 11.5.0.1.2: Error message
Comment
- The `Try Kill sFilePath` instruction triggers an error if, for example, the file to be deleted does not exist or the permission to delete the file is missing.
- In the example, the file path points to a file that does not exist, so an error is guaranteed to be raised. In this syntax in the above source code (one could say in the “evaluation context”), `Error` behaves like a global read-only variable managed by the interpreter.
- Assignments like `Error = …` are not possible, however, and result in a syntax error.
- This evaluation context includes constructs like `Variable = Error`, `Print Error` or `If Error then …`.
- Opposite the evaluation context is the statement context. If `Error` is syntactically in a place where a statement is expected, then it behaves like a statement. The following line is for demonstration purposes only:
If Error Then Error Error
- The first Error is in the “evaluation context”. Error stands for the above boolean variable.
- The second Error after `Then` is a statement - as it is in the statement context.
- The third Error stands for the value of the boolean variable Error.
- Short version: If the variable Error has the value True, then the instruction Error outputs the value Variable Error - i.e. True as expected.
- 11.5.0.1.5 Example for the use of the method Error.Raise(…)
- The value of a base for a number value system is to be read in via the property Textbox.Text, which is to be used in the programme. The following applies to the base: base ∈ ℤ (data type integer), base ≥ 2 and base ≤ 32. Here you can see the complete source code for the dialogue:
' 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
- The validation of the input takes place in the function CheckInput(argument).
- Errors that occur are raised by the programmer with suitably defined error message using the Error.Raise(error_description) method.
- If an error occurs with the assignment $iBasis = CheckInput(txbBasis.Text), the error is safely caught with CATCH and the corresponding user-defined error message is issued, otherwise the CATCH block is skipped.
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


