Exceptions

Top  Previous  Next

What is an exception?

An exception is simply a signal that something has gone wrong. Under object pascal all exceptions are represented by an exception object, which contains information about the error that has occurred. The most basic exception object simply contains a text string called message, which contains a string representation of the error that occurred.

The exception object is an object like everything else. It is normal to create your own exception objects if your error needs more information than just a string.
 

Handling errors in Smart Mobile Studio

Whilst we all want to spend our time writing functional code, errors will and do occur in in code from time to time.
In serious code you should handle error situations so that at the very least, the user is informed about the error in your chosen way.
SmartMS uses the event handling approach to error handling. Errors are (mostly) treated as exceptions, which cause program operation to suspend and jump to the nearest exception handler. If you don't have one, this will be the SmartMS default handler - it will report the error and terminate your program.
Often, you will want to handle the error, and continue with your program. For example, you may be trying to display a picture on a page, but cannot find it. So you might display a placeholder instead.

 

NOTES:

Except: Starts the error trapping clause of a Try statement.

Finally: Starts the unconditional code section of a Try statement.

On: Defines exception handling in a Try Except clause.

Try: Starts code that has error trapping.
 

 
TRY, EXCEPT where there are problems

 

SmartMS provides a simply construct for wrapping code with exception handling. When an exception occurs in the wrapped code (or anything it calls), the code will jump to the exception handling part of the wrapping code:

 

begin

Try

 // The code we want to execute

Except

 // This code gets executed if an exception occurs in the above block

end;

end;

 

The Except keyword is used to mark the start of a block of statements that handle an exception in a Try clause. If the Except block can handle the exception, then the program is not terminated.
We literally try to execute some code, which will run except when an error (exception) occurs. Then the except code will take over.

 

EXCEPT using Delphi:

·Version 1
In this version, if the Try clause generates an exception the Except clause is executed. This is used to take alternative action when something unexpected goes wrong. The except clause cannot determine the error type however. When the division fails, the code jumps to the except block. The first ShowMessage statement therefore does not get executed.
·Version 2
This is similar to version 1, but specifies different actions for different exception types, such as EInOutError. An Else clause can be used as a catch all for unexpected exception types. The general exception type Exception can be used to catch all exception types.
 
By assigning a Name to the exception, the message text of the exception (Name.Message) can be obtained for display or other uses.
 
When an exception is raised in a version 2 setup, if the exception is not acted upon by On or Else statements, then a check is made to see if we are in a nested Try block. If so, the Except clause of this parent Try is processed. If no On or Else clause is found, the program terminates.
 
The Else clause is not really necessary - it is better to use On E:Exception Do, the generic exception handling, since it still provides the error message (E.Message).

 

 

Catching exceptions with Smart Mobile Studio

 

Catching an exception is done inside a try / except block. If the code inside the block raises an exception then you can safely handle it before either exiting your method or continuing execution.

 

EXCEPT with Smart Mobile Studio

Please include the units SmartCL.System and System.Complex to use Exceptions. Differently from Delphi, SmartMS does not trap the error automatically. You have, firstly, check if a variable have unexpected type and then start the error trapping in the except block, which you can handle the exception.  Here is how you raise an exception:

 

 if (something <> what_you_expectthen

     raise exception.create('I expected something else');

 

 
Example code : Zero divide with a plain Except block in SmartMS

procedure TForm1.btnClearClick(Sender: TObject);

var

  number, zero : Integer;

begin

  // Try to divide an integer by zero - to raise an exception

  Try

    zero   := 0;

    number := 1 div zero;

 

    if IsInfinite(number) then raise Exception.Create('Division by zero'else

    ShowMessage('number / zero = '+FloatToStr(number));

  

  Except

    ShowMessage('Unknown error encountered');

  end;

end;

Result is: Unknown error encountered

 

mytoggle_plus1JS code:

function btnClearClick(Self, Sender$4) {

      var number = 0;

      var zero = 0;

      try {

         zero = 0;

         number = $Div(1,zero);

         if (IsInfinite(number)) {

            throw Exception.Create($New(Exception),"Division by zero");

         } else {

            alert("number / zero = "+((number)).toString());

         }

      } catch ($e) {

         alert("Unknown error encountered")      }

   }

 

 

Example code : Example code : Divide by zero with an Except On clause

procedure TForm1.W3Button1Click(Sender: TObject);

var

  number, zero : Integer;

begin

  // Try to divide an integer by zero - to raise an exception

  Try

    zero   := 0;

    number := 1 div zero;

    if IsInfinite(number) then raise Exception.Create('Division by zero'else

    ShowMessage('number / zero = '+IntToStr(number));

  Except

    on E : Exception do

     Begin

       ShowMessage(E.ClassName+' error raised, with message : '+E.Message);

     end;

  end;

end;

Result is: It will show a message Exception error raised with message : Division by zero
 

Important : you can determine the type of error that occured by using the generic exception handling - On E:Exception Do. E is a pointer to the exception object that is created by the exception condition. E.ClassName gives the exception type, such as 'EDivByZero', as shown in the final example code.

 

mytoggle_plus1JS code:

function W3Button1Click(Self, Sender$6) {

      var number$1 = 0;

      var zero$1 = 0;

      try {

         zero$1 = 0;

         number$1 = $Div(1,zero$1);

         if (IsInfinite(number$1)) {

            throw Exception.Create($New(Exception),"Division by zero");

         } else {

            alert("number / zero = "+number$1.toString());

         }

      } catch ($e) {

         var E = $W($e);

         alert(TObject.ClassName($Check(E,"").ClassType)+" error raised, with message : "+

                                                           $Check(E,"").FMessage)      }

   }

 

 

Being more selective

 

While catching all types of exception with the same error handler is very effective, there are times when you want to handle specific errors differently. Object Pascal allows you to handle that in the following way:

 

Example code : Custom Exception

EAbort = class(Exception);

 

 

{ Raise abort exception }

procedure DoSomething;

begin

  raise EAbort.Create('Abort without dialog');

end;

 

 

procedure TForm1.W3Button4Click(Sender: TObject);

var

  number, zero: Integer;

begin

  // Raise an selection exception

  try

    zero := 1;

    number := 0 div zero;

 

    if number = 0 then

      DoSomething;

 

    if IsInfinite(number) then

      raise Exception.Create('Division by zero')

    else

    begin

      ShowMessage('number / zero = ' + FloatToStr(number));

 

    end;

  except

    on e: EAbort do

      ShowMessage('A different error occured: ' + e.message);

 

    on e: exception do

      ShowMessage('All other errors are handled by this one: ' + e.message);

  end;

end;

Result is: It will show a message A different error occured: Abort without dialog
 

mytoggle_plus1JS code:

function W3Button4Click(Self, Sender$9) {

      var number$4 = 0;

      var zero$4 = 0;

      try {

         zero$4 = 1;

         number$4 = $Div(0,zero$4);

         if (!number$4) {

            DoSomething();

         }

         if (IsInfinite(number$4)) {

            throw Exception.Create($New(Exception),"Division by zero");

         } else {

            alert("number / zero = "+((number$4)).toString());

         }

      } catch ($e) {

         if ($Is($e,EAbort)) {

            var e$11 = $W($e);

            alert("A different error occured: "+$Check(e$11,"").FMessage)}

         else if ($Is($e,Exception)) {

            var e$12 = $W($e);

            alert("All other errors are handled by this one: "+$Check(e$12,"").FMessage)}

         else throw $e

      }

   }

 

 

Ignoring exceptions

 

Unless you handle an exception it causes the execution of the current procedure (recursively back to the first caller) to be aborted. In other words, it will stop running your code and propagate the error upwards. So if you don’t handle errors, it will report it to the method that called your code, or exit that and continue until it finds an exception handler. If you simply want to ignore any errors and continue no matter what (not recommended) you can do as such:

 

Example code : Divide by zero with no message error

procedure TForm1.W3Button1Click(Sender: TObject);

var

  number, zero : Integer;

begin

  // Try to divide an integer by zero - to raise an exception

  Try

    zero   := 0;

    number := 1 div zero;

    if IsInfinite(number) then raise Exception.Create('Division by zero'else

    ShowMessage('number / zero = '+IntToStr(number));

  Except

    on E : Exception do; // Ignore any error and continue

  end;

end;

Result is: neither messages nor console error

 

mytoggle_plus1JS code:

function W3Button1Click(Self, Sender$6) {

      var number$1 = 0;

      var zero$1 = 0;

      try {

         zero$1 = 0;

         number$1 = $Div(1,zero$1);

         if (IsInfinite(number$1)) {

            throw Exception.Create($New(Exception),"Division by zero");

         } else {

            alert("number / zero = "+number$1.toString());

         }

      } catch ($e) {

         var E = $W($e);

         /* null */

      }

   }

 

 
And FINALLY ...

 

Suppose that instead of trapping the error where it occurs, you may want to let a higher level exception handler in your code to do a more global trapping. Delphi provides an alternative part to the exception wrapper the Finally clause. Instead of being called when an exception occurs, the finally clause is always called after part or all of the try clause is executed. However, it does not trap the error - the next highest exception handling (try) block that we are nested in is located and executed.
 

Example code : Zero divide with a finally clause

procedure TForm1.W3Button3Click(Sender: TObject);

var

  number, zero : Integer;

begin

  // Try to divide an integer by zero - to raise an exception

  Try

    zero   := 0;

    number := 1 div zero;

    if IsInfinite(number) then raise Exception.Create('Division by zero'else

    ShowMessage('number / zero = '+IntToStr(number));

  Finally

    if number = -1 then

    begin

      ShowMessage('Number was not assigned a value - using default');

      number := 0;

    end;

  end;

end;

Result is:

 Number was not assigned a value - using default

 Then, the program terminates with an Uncaught #<Object> console message - the finally clause did not trap the error.

 

mytoggle_plus1JS code:

function W3Button3Click(Self, Sender$8) {

      var number$3 = 0;

      var zero$3 = 0;

      try {

         zero$3 = 0;

         number$3 = $Div(1,zero$3);

         if (IsInfinite(number$3)) {

            throw Exception.Create($New(Exception),"Division by zero");

         } else {

            alert("number / zero = "+number$3.toString());

         }

      } finally {

         if (number$3==(-1)) {

            alert("Number was not assigned a value - using default");

            number$3 = 0;

         }

      }

   }

 

 

Example code : Zero divide with a finally clause and Except

procedure TForm1.W3Button2Click(Sender: TObject);

var

  number, zero: Integer;

begin

  // Try to divide an integer by zero - to raise an exception

  try

    try

      zero := 0;

      number := 1 div zero;

      if IsInfinite(number) then raise Exception.Create('Division by zero')

      else

        ShowMessage('number / zero = ' + IntToStr(number));

    except

      number := -1;

    end;

  finally

    if number = -1 then

    begin

      ShowMessage('Number was not assigned a value - using default');

      number := 0;

    end;

  end;

  WriteLn('number is: '+IntToStr(number));

end;

Result is:

 Number was not assigned a value - using default

 Then, the program display console message number is 0. The program trap the error and set number to -1

 

 

mytoggle_plus1JS code:

function W3Button2Click(Self, Sender$7) {

      var number$2 = 0;

      var zero$2 = 0;

      try {

         try {

            zero$2 = 0;

            number$2 = $Div(1,zero$2);

            if (IsInfinite(number$2)) {

               throw Exception.Create($New(Exception),"Division by zero");

            } else {

               alert("number / zero = "+number$2.toString());

            }

         } catch ($e) {

            number$2 = -1;

         }

      } finally {

         if (number$2==(-1)) {

            alert("Number was not assigned a value - using default");

            number$2 = 0;

         }

      }

      WriteLn(("number is: "+number$2.toString()));

   }

 

 

Try-Finally is normally used by a routine to allow cleanup processing to take place, such as freeing resources, with the exception being correctly passed to the caller to handle. In this example, a exception is trapped.