Structured Error Handling
Visual FoxPro provides the TRY...CATCH…FINALLY control structure to handle errors, or exceptions, that can occur in your code at run time.
TRY...CATCH…FINALLY
TRY…CATCH…FINALLY contains code blocks for the TRY, CATCH, and FINALLY statements. In these code blocks, you can perform the following error handling tasks:
- Generate an exception
- Catch an exception
- Nest TRY…CATCH blocks
- Escalate an exception
- Exit TRY…CATCH…FINALLY immediately
- Use commands
- Run final statements
The TRY...CATCH…FINALLY structure starts with the TRY statement, which marks the beginning of the TRY block. In the TRY block, you can specify a set of code statements that can produce errors at run time. If your program completes the TRY block without generating an exception, it skips the CATCH block and looks for the FINALLY block near the end of the structure, if it exists, and runs the corresponding statements. If the FINALLY block does not exist, the program moves outside the structure to the first statement after the ENDTRY statement, which marks the end of the TRY…CATCH…FINALLY structure.
Generating an Exception
If an error occurs in the TRY block, Visual FoxPro generates an exception, which is a condition that causes your computer to handle the error in a separate routine. When the error occurs, the code "throws" an exception, and appropriate statements in the CATCH block handle the specific exception. After the program resolves the error, the program moves to the FINALLY block to run any remaining statements and leaves the TRY…CATCH…FINALLY structure.
Catching an Exception
When an error occurs in the TRY block, Visual FoxPro generates an exception, and the program moves to the first CATCH statement. The program looks for CATCH statements only when an error occurs. You can specify any number of CATCH statements, including none. The program examines CATCH statements in the order that they appear in the CATCH block to see if one of them can handle the exception. If the program finds a CATCH statement that handles the exception, the program runs the corresponding code.
The CATCH statement can contain optional TO and WHEN clauses. You can specify a memory variable in the TO clause to store a reference to an Exception object, which is created only when an exception occurs. If you want to establish a condition for a CATCH block to run, you can specify an expression in the WHEN clause that must evaluate to True (.T.) before the CATCH block runs. CATCH statements work similarly to CASE statements in that the WHEN clause must evaluate to a logical expression. If the TO and WHEN clauses do not exist, the CATCH statement evaluates as CATCH WHEN .T. (True).
After the program runs statements in a CATCH block, it does not return to the TRY statement, and it does not look for other CATCH statements. Instead, the program proceeds directly to the FINALLY statement, if it exists. Otherwise, the program moves to the statement that immediately follows ENDTRY.
Note Visual FoxPro permits the existence of TRY...CATCH…FINALLY structures that do not have CATCH and FINALLY blocks. Visual FoxPro simply throws the generated error to a higher-level error handler.
Priority of Error Handlers
If an error occurs in the method of an object that is called from a TRY block, Visual FoxPro follows the error handling procedure for that particular object. This protocol provides a way for you to maintain encapsulation and control of your components. For example, suppose the line MyForm.Refresh()
in a TRY block produces an error. If code for an Error event exists to handle the error, then the Error event takes precedence. If no Error event exists, then a CATCH statement attempts to handle the error.
The priority of error handlers for object code is described as follows:
- TRY…CATCH in the method where the exception occurs. This applies to external procedures, not methods, that are called in the method.
- Error event, if it exists
- TRY...CATCH that is higher in the calling chain and can exist in a higher level method
- Visual FoxPro system error handler
Caution The use of TRY...CATCH in a method for a class that does not have an Error event might impact existing code and possibly break backward compatibility. Therefore, TRY...CATCH statements appearing in methods should only call other methods of classes that contain an Error event.
The following example shows how the line of code, ? "BBB"
, never executes due to calling myMethod2
from myMethod1
without an Error event for myMethod2
. However, if myMethod2
was called directly, ON ERROR DO errorHandler
handles the error and returns execution back to the program so that ? "BBB"
can execute.
ON ERROR DO errorHandler
x=CREATEOBJECT("myClass")
x.myMethod1()
DEFINE CLASS myClass AS Session
PROCEDURE myMethod1
TRY
THIS.myMethod2()
CATCH TO oErr
? "Catch:",oErr.ErrorNo
ENDTRY
ENDPROC
PROCEDURE myMethod2
? "AAA"
x=y && The variable y does not exist.
&& CATCH traps this error.
? "BBB" && Does not execute if CATCH traps previous error.
ENDPROC
ENDDEFINE
If an error occurs in a chain of TRY…CATCH blocks within the method of an object, the following rules apply:
If the code itself in the TRY block generates an exception, the CATCH statement handles the exception. If no CATCH statement exists to handle the exception, the Error event handles the exception. Otherwise, Visual FoxPro escalates the error to an appropriate handler. The same behavior applies if the error occurs with an external procedure that is called from a TRY block. For example:
x=CREATEOBJECT("myClass") x.myMethod() DEFINE CLASS myClass AS Session PROCEDURE myMethod TRY DO myProc CATCH TO oErr ? "Catch:",oErr.Errorno ENDTRY ENDPROC PROCEDURE Error(nError, cMethod, nLine) ? "Error:",nError ENDPROC ENDDEFINE PROCEDURE myProc x=y && The variable y does not exist. CATCH handles this error. ENDPROC
If the method of an object that is called from the TRY block generates an exception, the Error event for the object's method handles the exception. This maintains object encapsulation because this method could have been called from another method. For example:
x=CREATEOBJECT("myClass") x.myMethod1() DEFINE CLASS myClass AS Session PROCEDURE myMethod1 TRY THIS.myMethod2() CATCH TO oErr ? "Catch:",oErr.Errorno ENDTRY ENDPROC PROCEDURE myMethod2 x=y && Error event handles this error. ENDPROC PROCEDURE Error(nError, cMethod, nLine) ? "Error:",nError ENDPROC ENDDEFINE
Nesting TRY…CATCH Blocks
- You can nest TRY...CATCH blocks. That is, you can insert a TRY...CATCH…FINALLY structure within a TRY, CATCH, or FINALLY block.
Escalating an Exception
You can include a THROW statement if you want to "rethrow" or escalate the exception to a higher-level error handler. You can call THROW from any code block of a TRY…CATCH…FINALLY structure. However, you can use THROW anywhere in code where an error handler exists to catch the exception. You cannot call THROW from the Command window.
Caution Calling the THROW command outside the TRY...CATCH...FINALLY structure and without an appropriate error handler causes your program to quit.
If you call THROW outside the TRY...CATCH section where the thrown exception is handled by an ON ERROR routine or the Visual FoxPro system handler instead of an Error event, program execution does not return to the location where the THROW statement occurs. The following example shows how the line of code, ?2
, does not execute. This behavior differs from a traditional error behavior in which program execution returns to the location where the error occurred:
ON ERROR ? ERROR()
DO myProc1
PROCEDURE myProc1
?1
THROW 11
?2
ENDPROC
Remember, the intention for calling THROW is to escalate the exception to a higher-level error handler.
If an outer TRY...CATCH block exists around a THROW statement when it is called from within a CATCH or FINALLY block, Visual FoxPro reassigns the memory variable specified in the TO clause from the object reference of the previous exception object to an object reference for the most recent exception object created by the thrown exception. If no outer TRY...CATCH structure exists, Visual FoxPro rethrows the exception to an ON ERROR routine or an error event, if they exist. Otherwise, Visual FoxPro calls the system handler.
Exiting TRY…CATCH…FINALLY
You can include an EXIT statement in a TRY or CATCH block to break out of the code block immediately. The program resumes running code at the FINALLY statement, if one exists, or at the line immediately following the ENDTRY statement.
Using Commands in TRY…CATCH…FINALLY
You can use different Visual FoxPro commands that affect how code normally runs from within a TRY or CATCH block. However, if you use one of these commands that are not permitted for a specific block, Visual FoxPro generates an error at run time. The FINALLY statements always run before handling these commands except when CANCEL or QUIT is used. The following table lists the Visual FoxPro commands you can and cannot use in a TRY, CATCH, or FINALLY block.
Command | TRY | CATCH | FINALLY |
---|---|---|---|
CANCEL | Yes | Yes | Yes |
CLEAR ALL | Yes | No | No |
CLOSE ALL | Yes | Yes | Yes |
DOEVENTS | Yes | Yes | Yes |
ERROR | Yes | Yes | Yes |
EXIT | Yes | Yes | Yes |
LOOP | No | No | No |
QUIT | Yes | Yes | Yes |
RELEASE | Yes | Yes | Yes |
RESUME | Yes | Yes | Yes |
RETRY | No | No | No |
RETURN | No | No | No |
RETURN TO MASTER | No | No | No |
RETURN TO <ProcedureName> | No | No | No |
SET STEP ON | Yes | Yes | Yes |
SUSPEND | Yes | Yes | Yes |
THROW | Yes | Yes | Yes |
Note You can include commands such as LOOP in a block as long as you place it inside an embedded control statement such as FOR EACH...ENDOR or DO WHILE…ENDDO. You can call the ERROR command from the TRY, CATCH, or FINALLY blocks. TRY...CATCH…FINALLY handles the ERROR command like any other exception or use of the THROW command.
Running Final Statements
If the program finds the FINALLY statement, it runs the code that is in the FINALLY block. The FINALLY block usually cleans up any resources allocated by the TRY block and is always the last code to run before control leaves the TRY...CATCH…FINALLY structure.
The FINALLY block always runs when an exception, unhandled or otherwise, occurs, even if another exception handler such as the ON ERROR routine or an error event for an object, is in effect.
Note You cannot explicitly transfer control of your program directly to a CATCH or FINALLY block. You cannot bypass the FINALLY statement if it exists unless you use the CANCEL command to stop the program from continuing. If you wish to bypass the FINALLY statement, you need to provide code at the beginning of the FINALLY block that checks for a condition and breaks out of the block if that condition is met.
After the FINALLY block runs, the program then proceeds to the statement immediately following the ENDTRY statement. The ENDTRY statement ends the TRY...CATCH…FINALLY structure.
Exception Objects
When an error occurs in the TRY block, Visual FoxPro automatically creates an Exception object, which contains details about the error.**A CATCH statement usually handles this exception, which is referenced by the memory variable specified by VarName in the TO clause.
The Exception object always exists as an instance of the Exception base class. You cannot run user code when the Exception object is created. If you wish to use a subclass of the Exception class, you should rethrow the exception in the CATCH or FINALLY block.
If an error or exception occurs in the CATCH or FINALLY block, the TRY...CATCH…FINALLY structure itself does not handle this exception. Instead, Visual FoxPro passes errors that occur in the CATCH or FINALLY blocks to an outer error handler, such as an error event or the ON ERROR command, which can include another TRY...CATCH…FINALLY structure. The outer handler takes care of errors in the event that TRY...CATCH…FINALLY does not contain any CATCH statements or if no CATCH statements exist that can handle the error. If you want to have a TRY...CATCH…FINALLY structure that catches all errors, make sure you have a CATCH statement that always evaluates to True (.T.).
Note Only Visual FoxPro program (.prg) files support Exception objects.
See Also
New Classes, Commands, and Functions | Testing and Debugging Applications | TRY...CATCH…FINALLY Command | Exception Class