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