Visual FoxPro Structured Error Handling Matrix

Randy Pearson, Cycla Corporation, December 27, 2002 [Updated October 18, 2006]

Below is a matrix that provides a reference for VFPs error handling strategies. The columns depict each of eight different error triggering event types, while the rows signify the most immediate type of error handling that is in effect higher in the calling chain. There is also a sample program at the bottom of the page that you can cut and paste into VFP to try out these combinations. Simply call the program passing a numeric parameter with the Case number as shown in the matrix.

Immediate
Calling Environment:

Call from an Outer TRY/CATCH(c) (g) Call from Object with Error() Method(d) (g) Call with an ON ERROR Routine in Effect No Other Error Handling
Triggering Event
TRY block runs without error [Case 0] Any code in FINALLY block is executed(a), and then processing continues with code after ENDTRY.
TRY exception handled by a CATCH (no THROW) [Case 1] Remainder of TRY block is aborted. Code in applicable CATCH block is run. Any code in FINALLY block is then executed(a), after which processing continues with code after ENDTRY.
Including a THROW in the CATCH block [Case 2] FINALLY code is run, but code after ENDTRY is not run.

Error 2071 is potentially caught and handled by outer block. Outer FINALLY block also executes. Note that you could re-THROW some exceptions even higher in the calling chain.(c) [Case 12]

Any code in FINALLY block is executed first(!)(a) and then Error 2059, "Unhandled Structured Exception," is generated and trapped by Error() method of calling object. MESSAGE() contains parsed info on thrown exception, which generated Error 2071, "User Thrown Error."(g) [Case 102] Any code in FINALLY block is executed first(!)(a) and then Error 2059, "Unhandled Structured Exception," is generated and trapped by ON ERROR routine. MESSAGE() contains parsed info on thrown exception, which generated Error 2071, "User Thrown Error."(g) [Case 1002] Any code in FINALLY block is executed first(!)(a) and then Error 2059, "Unhandled Structured Exception," is generated, caught by VFP which shows the usual message box with parsed info on thrown exception, which generated Error 2071, "User Thrown Error."(g) [Case 2]
TRY exception not handled [Case 3] First the FINALLY block is executed, and then the outer CATCH (and then outer FINALLY, etc.) block is executed.(c) [Case 13]

Important: The error that is "caught" by the outer block is the original error (vs. Error 2059), as this is proper exception handling.

Any code in FINALLY block is executed(a) and then Error 2059, "Unhandled Structured Exception," is generated and trapped by Error() method of calling object. MESSAGE() contains parsed info on original exception, but you cannot get at true original exception object. [Case 103] Any code in FINALLY block is executed(a) and then Error 2059, "Unhandled Structured Exception," is generated and trapped by ON ERROR routine. MESSAGE() contains parsed info on original exception, but you cannot get at true original exception object. (e) [Case 1003] Any code in FINALLY block is executed(a) and then Error 2059, "Unhandled Structured Exception," is generated and caught by VFP, which shows a message box with parsed information on the original exception. [Case 3]
Second Error in CATCH code [Case 4] First the FINALLY block is executed, and then the second error is caught by the outer block, so the outer CATCH (and then outer FINALLY, etc.) block is executed. The original error is completely masked by the second error.(c) [Cases 14 and 15] Any code in FINALLY block is executed(a) and then Error 2059, "Unhandled Structured Exception," is generated and trapped by Error() method of calling object. MESSAGE() contains parsed info on the second error.  The original error is completely masked by the second error. [Cases 104 and 105] Any code in FINALLY block is executed(a) and then Error 2059, "Unhandled Structured Exception," is generated and trapped by ON ERROR routine. MESSAGE() contains parsed info on the second error.  The original error is completely masked by the second error. [Cases 1004 and 1005] Any code in FINALLY block is executed(a) and then Error 2059, "Unhandled Structured Exception," is generated and caught by VFP, which shows a message box with parsed information on the second error.  The original error is completely masked by the second error. [Cases 4 and 5]
Second Error in FINALLY code [Case 5]
Error in FINALLY code (no original error)(f) [Case 6] First the FINALLY block is executed, and then the error is caught by the outer block.(c) [Case 16]

Important: The error that is "caught" by the outer block is the original error (vs. Error 2059), as this is technically proper exception handling. Note: There is no difference, as seen by the outer block, between unhandled errors in TRY code and errors in FINALLY code of the inner block.

Any code in FINALLY block is executed(a) and then Error 2059, "Unhandled Structured Exception," is generated and trapped by Error() method of calling object. MESSAGE() contains parsed info on original exception, but you cannot get at true original exception object. [Case 106] Any code in FINALLY block is executed(a) and then Error 2059, "Unhandled Structured Exception," is generated and trapped by ON ERROR routine. MESSAGE() contains parsed info on original exception, but you cannot get at true original exception object. (e) [Case 1006] Any code in FINALLY block is executed(a) and then Error 2059, "Unhandled Structured Exception," is generated and caught by VFP, which shows a message box with parsed information on the original exception. [Case 6]
Error outside TRY block [Case 7] Error properly handled by outer TRY block. [Case 17] Error properly handled by Error() method of calling object. [Case 107] Error properly handled by ON ERROR routine. [Case 1007] Error properly handled by VFP. [Case 7]
Error in called COM object. Depends on whether COMRETURNERROR is used in called object. See discussion by Donnael Consulting at http://www.donnael.com/tips.htm#CRETest.
RETURN TO MASTER
RETURN TO SomeModule
If the RETURN TO point is higher in the call stack than an intervening TRY/CATCH block, then VFP error 2060 is triggered. In VFP 8, error 2060 was not trappable, which would result in a VFP error dialog--a killer for server-based apps. Starting with VFP 9, error 2060 is trappable, so if you design carefully, it may be possible to cope; nevertheless, you still cannot RETURN TO through an intermediate TRY/CATCH. No unexpected effect.

Footnotes

  1. Important! Code in FINALLY block executes after either success or failure of TRY block. But any code after ENDTRY does not execute when program control exits the TRY block (either because of a THROW or an unhandled exception).
  2. It appears CATCH TO loExc does not require a LOCAL command in order to protect variables named loExc higher in the calling stack! That is, it seems that CATCH TO performs an implicit PRIVATE statement. This is not true of other VFP constructions, such as FOR EACH.
  3. If an outer TRY..ENDTRY is triggered, the effect is similar to a RETURN TO the module with that block. In other words, all intermediate modules in the calling chain are aborted immediately. This means garbage collection code on released objects will fire before the actual code in the outer TRY.
  4. In all cases for this column, if an ON ERROR routine is also in effect, it is never triggered.
  5. MESSAGE(2) often equals "ENDTRY" in the case of unhandled structured exceptions.
  6. Note that Case 3 and Case 6 are seen as virtual equivalents by calling code, irrespective of the choice of calling environments.
  7. Important Note: An outer TRY/CATCH is ineffective in handling errors during a call to any method of an object that has its own Error() method. In this case, even a deliberate THROW (or an unhandled exception) in a lower TRY block will not reach the outer TRY/CATCH--instead error 2059 or 2071 will be trapped by the intervening Error() method (see Case 102). This is true whether the actual error occurs in a method of the object with an Error() method or in a subsequent method or UDF called by that object.
  8. See http://fox.wikis.com/wc.dll?Wiki~ThrowExceptionRevisited for an interesting approach to manipulating exception objects.

Implications

  1. Code within CATCH and FINALLY blocks must be very clean! When a second error occurs in these places, the original (presumably more important) error is masked.
  2. Although coordinated nesting of TRY..ENDTRY blocks throughout appears best, VFP does a very good job of providing reasonable behaviors when combinations of error handling techniques are utilized.
  3. You can use SYS(2410) in CATCH code to determine whether an outer TRY is available. You could choose between using THROW or something else (such as re-raising with ERROR), depending on the value returned by SYS(2410).
  4. Almost all uses of RETURN TO <somewhere> can now be replaced by much sounder structured error handling code.
  5. Even more than before, we can avoid placing Error() methods in classes whose objects don't get direct access to the UI.
  6. Whether code after ENDTRY is run in these cases depends on whether you RETURN (or RETRY) from the non-structured error-handling (or choose Ignore from VFP).
  7. Mixing Error() methods with structured exception handling can be tricky in a framework design. As TRY/CATCH usage propogates into refactored designs, some previous Error() methods may tend to be eliminated.

Practice Code (refer to Case numbers in matrix above)

Simply call the program passing a numeric parameter with the Case number as shown in the matrix.