Handling Run-Time Errors

Run-time errors occur after the application starts to execute. Actions that would generate run-time errors include: writing to a file that doesn't exist, attempting to open a table that is already open, trying to select a table that has been closed, encountering a data conflict, dividing a value by zero, and so on.

At times, errors occur when users run your application. You can call your own error-handling routine by including ON ERROR. Typically, ON ERROR uses a DO command to run a routine that handles the error, as in:

ON ERROR DO My_Error

If your application contains no error-handling routines when an error occurs, the application pauses and Visual FoxPro displays an error message with the following options:

  • Cancel   If a user chooses Cancel, Visual FoxPro immediately stops running the application and returns control to the system.
  • Ignore   If a user chooses Ignore, Visual FoxPro ignores the line that caused the error and continues to the next line in the program.

For a complete list and explanation of Visual FoxPro error messages, see Error Messages.

Tip   Be sure to provide documentation to your users that describes the errors that they might see, and suggests ways in which they can correct errors.

The following commands and functions are useful when anticipating and managing run-time errors.

To Use
Fill an array with error information AERROR( )
Open the Debugger or Trace window DEBUG or SET STEP ON
Generate a specific error to test your error handling ERROR
Return an error number ERROR( )
Return an executing program line LINENO( )
Return an error message string MESSAGE( )
Execute a command when an error occurs ON ERROR
Return commands assigned to error handling commands ON( )
Return the name of the currently executing program PROGRAM( ) OR SYS(16)
Re-execute the previous command RETRY
Return any current error message parameter SYS(2018)

Anticipating Errors

The first line of defense against run-time errors is anticipating where they could occur and coding around them. For example, the following line of code moves the record pointer to the next record in the table:

SKIP

This code works unless the record pointer is already past the last record in the table, at which point an error will occur.

The following lines of code anticipate this error and avoid it:

IF !EOF()
   SKIP
      IF EOF()
         GO BOTTOM
      ENDIF
ENDIF

As another example, the following line of code displays the Open dialog box to allow a user to open a table in a new work area:

USE GETFILE('DBF') IN 0

The problem is that the user could choose Cancel in the Open dialog box or type the name of a file that doesn't exist. The following code anticipates this by making sure that the file exists before the user tries to use it:

cNewTable = GETFILE('DBF')
IF FILE(cNewTable)
   USE (cNewTable) IN 0
ENDIF

Your end user may also type the name of a file that isn't a Visual FoxPro table. To circumvent this problem, you could open the file with low-level file I/O functions, parse the binary header, and make sure that the file is indeed a valid table. However, this would be a bit of work and it might noticeably slow down your application. You would be better off handling the situation at run time by displaying a message like "Please open another file. This one is not a table" when error 15, "Not a table," occurs.

You can't, and probably don't want to, anticipate all possible errors, so you'll need to trap some by writing code to be executed in the event of a run time error.

Handling Procedural Errors

When an error occurs in procedural code, Visual FoxPro checks for error-handling code associated with an ON ERROR routine. If no ON ERROR routine exists, Visual FoxPro displays the default Visual FoxPro error message. For a complete list of Visual FoxPro error messages and error numbers, see Help.

Creating an ON ERROR routine

You can include any valid FoxPro command or expression after ON ERROR, but normally you call an error-handling procedure or program.

To see how ON ERROR works, you can type an unrecognizable command in the Command window, such as:

qxy

You'll get a standard Visual FoxPro error message dialog box saying "Unrecognized command verb." But if you execute the following lines of code, you'll see the error number, 16, printed on the active output window instead of the standard error message displayed in a dialog box:

ON ERROR ?ERROR()
qxy

Issuing ON ERROR with nothing after it resets the built-in Visual FoxPro error messaging:

ON ERROR

In skeletal form, the following code illustrates an ON ERROR error handler:

LOCAL lcOldOnError

* Save the original error handler
lcOldOnError = ON("ERROR")

* Issue ON ERROR with the name of a procedure
ON ERROR DO errhandler WITH ERROR(), MESSAGE()

* code to which the error handling routine applies

* Reset the original error handler
ON ERROR &lcOldOnError

PROCEDURE errhandler
LOCAL aErrInfo[1]
AERROR(aErrInfo)
DO CASE
   CASE aErrInfo[1] = 1 && File Does Not Exist
      * display an appropriate message
      * and take some action to fix the problem.
   OTHERWISE
      * display a generic message, maybe
      * send high priority mail to an administrator
ENDPROC

Handling Errors in Classes and Objects

When an error occurs in method code, Visual FoxPro checks for error-handling code associated with the Error event of the object. If no code has been written at the object level for the Error event, the Error event code inherited from the parent class, or another class up the class hierarchy, is executed. If no code has been written for the Error event anywhere in the class hierarchy, Visual FoxPro checks for an ON ERROR routine. If no ON ERROR routine exists, Visual FoxPro displays the default Visual FoxPro error message.

Note   The Error event does not occur if an ON ERROR routine is on the call stack.

The beauty of classes is that you can encapsulate everything a control needs, including error handling, so that you can use the control in a variety of environments. Later, if you discover another error the control might encounter, you can add handling for that error to the class, and all objects based on your class will automatically inherit the new error handling.

For example, the vcr class in the Buttons.vcx class library, located in the Visual FoxPro ...\Samples\Classes directory, is based on the Visual FoxPro container class.

Four command buttons in the container manage table navigation, moving the record pointer in a table with the following commands:

GO TOP
SKIP - 1
SKIP 1
GO BOTTOM.

An error could occur when a user chooses one of the buttons and no table is open. Visual FoxPro attempts to write buffered values to a table when the record pointer moves. So an error could also occur if optimistic row buffering is enabled and another user has changed a value in the buffered record.

These errors could occur when the user chooses any one of the buttons; therefore, it doesn't make sense to have four separate error handling methods. The following code associated with the Error event of each of the command buttons passes the error information to the single error-handling routine of the class:

LPARAMETERS nError, cMethod, nLine
THIS.Parent.Error(nError, cMethod, nLine)

The following code is associated with the Error event of the vcr class. The actual code differs because of coding requirements for localization.

Parameters nError, cMethod, nLine
DO CASE
CASE nError = 13 && Alias not found
   cNewTable = GETFILE('DBF')
   IF FILE(cNewTable)
      SELECT 0
      USE (cNewTable)
      This.SkipTable = ALIAS()
   ELSE
      This.SkipTable = ""
   ENDIF
CASE nError = 1585 && Data Conflict
* Update conflict handled by a datachecker class 
   nConflictStatus = ;
      THIS.DataChecker1.CheckConflicts()
   IF nConflictStatus = 2
      MESSAGEBOX "Can't resolve a data conflict."
   ENDIF
OTHERWISE
* Display information about other errors.

   cMsg="Error:" + ALLTRIM(STR(nError)) + CHR(13) ;
      + MESSAGE()+CHR(13)+"Program:"+PROGRAM()

   nAnswer = MESSAGEBOX(cMsg, 2+48+512, "Error")
   DO CASE
      CASE nAnswer = 3   &&Abort
         CANCEL
      CASE nAnswer = 4   &&Retry
         RETRY
      OTHERWISE       && Ignore
         RETURN
   ENDCASE
ENDCASE

You want to make sure that you supply information for an error that you haven't handled. Otherwise, the error event code will execute but won't take any actions, and the default Visual FoxPro error message will no longer be displayed. You, as well as the user, won't know what happened.

Depending on your target users, you might want to supply more information in the case of an unhandled error, such as the name and phone number of someone to call for help.

Returning from Error Handling Code

After the error handling code executes, the line of code following the one that caused the error is executed. If you want to re-execute the line of code that caused the error after you've changed the situation that caused the error, use the RETRY command.

Note   The Error event can be called when the error encountered wasn't associated with a line of your code. For example, if you call a data environment's CloseTables method in code when AutoCloseTables is set to true (.T.) and then release the form, an internal error is generated when Visual FoxPro tries to close the tables again. You can trap for this error, but there is no line of code to RETRY.

Using Shutdown Routines

Create your own shutdown routine by including the command ON SHUTDOWN in your code. Typically, ON SHUTDOWN uses a DO command to call a routine if you try to exit the application, as in the following example:

ON SHUTDOWN DO My_Shutdown

This routine typically includes a dialog box that asks if the user is sure they want to quit the current application. If the user wants to quit the application, the routine can close open files and clean up the environment, and then issue the QUIT command. If the user doesn't want to exit the current application, the routine can return control to the application.

See Also

The Logging of Code Coverage | Output Display | Testing and Debugging Applications | ON ERROR | Seeing Stored Values