cObject
---cBaseErrorHandler
------cWindowsErrorHandler
---------ErrorSystem
This class is obsolete. It has been replaced with the cWindowsErrorHandler and cWebErrorHandler classes.
The DfError.pkg package creates the class ErrorSystem and an error-handling object based on this class. This object supports DataFlex's interactive interface for all errors generated in a program.
Use DFError.pkg
An instance of this class named Error_Object_Id is created by use of the DFError package. You should not create an instance of this class by any other means. When the object is created, its object ID is placed in the global Integer error_object_id. All access to this object should be through this Integer ID and not through the object's name. This package is a standard part of DFAllEnt.pkg, so there is usually no need to explicitly add this package to your application.
In most applications, you will not need to make any changes in the error handling as supplied and described here. This handler works well for most data-entry programs. The DataFlex report- and batch-processing objects provide their own custom error-redirection logic to handle batch errors, and can be easily customized. The information provided below is provided for advanced usage.
A global integer named error_object_id identifies the object that should receive errors. Anytime an error occurs, the error_report message is sent to the object identified by this global integer. It is expected this will handle the error as required.
When the standard error package, DfError.Pkg, is used, a global error object is created and its object ID is moved to error_object_id. Anytime an error occurs, the message error_report is sent to the error object.
Never call error_report directly to generate an error. Errors should be generated through the use of the error command. The message error_report may be sent if the sending object is itself an error object. This occurs when a custom error handler within an object has been created to handle a process.
DataFlex distinguishes between two types of errors - user errors and unhandled errors.
When handled error occurs, a message box is presented with text with no technical information - no instruction address, no error number. If error numbers are meaningful to your application, you can choose to display error numbers by setting the pbShowErrorNumber property. The default caption text for a user error is "Error", which can be changed by setting the psUserErrorCaption property.
When an unhandled error occurs, a completely different dialog is presented. This dialog provides a complete message stack dump. If the error occurs at a deployment site, the error stack can be easily copied to the clipboard, added to an email message and sent to you. If the error occurs while you are testing (debugging) your application a "Debug" button will appear in the error dialog. Press this button and the application will break and enter debug mode at the error source. The default caption text for an unhandled error is the name of the executable followed by "Unhandled Program Error", which can be changed by setting the psUnhandledErrorCaption property.
The ErrorSystem object determines if an error is a user error by checking the error against a list of user error numbers. The class has a pre-defined list, which may be customized using the AddUserError, RemoveUserError and RemoveAllUserErrors methods.
A special global procedure, UserError, is defined along with this class definition and it should be used to generate user errors. While the Error command can be used to generate unhandled errors or user errors, it will mostly be used to generate unhandled errors. Note that many if not most of the unhandled errors will be generated by the runtime or the framework.
The following is an example of generating a user error.
Function Validate_Save Returns Integer
Integer iRetVal
Forward Get Validate_Save To iRetVal
If iRetVal Function_Return iRetVal
If (Invt.On_Hand < 0) Begin
Send UserError "Insufficient Inventory Stock" ""
Function_Return 1
End
End_Function
The following is an example of generating an unhandled error.
Procedure ProcessList
String[] ArrayOfNames
Integer iNames
Get GetUserNames to ArrayOfNames
Move (SizeOfArray(ArrayOfNames)) to iNames
If (iNames=0) Begin
Error DFERR_PROGRAM "GetUserNames should never have 0 names"
Procedure_Return
End
:
End_Procedure
Note that unhandled errors are not supposed to happen. The above example show how the Error command is used to identify and track down problems in an application's logic.
Prior to revision 14.1, the error handling system did not distinguish between user errors and handled errors. All errors were reported with the error number and the instruction address and a debug stack was never presented. If you need your application to behave exactly as it did in prior revisions you can set the pbUnhandledErrorSupport to False.
An error can be fatal, ignored, or trapped. If an error is fatal, an error message is displayed and the program is terminated. If an error is trapped, it is displayed with a pop up error dialog. If an error is ignored, it is ignored. When initialized, the error system will trap all errors except Error 41 (Find past end of file) and Error 42 (Find prior to beginning of file). You may choose to trap or ignore other errors. You may change this through the use of the trap_error, ignore_error, trap_all, and ignore_all messages.
There is a single error that will never be passed to the error object, Error 10 (Out of memory). Since the message-passing mechanism uses dynamically allocated memory, this would most-certainly cause a system failure.
You can turn any object into an error-handling object. This is done as follows:
1. The global integer error_object_id must be assigned to this object. Usually this assignment is temporary.
2. The Integer Property error_processing_state must be created within the object and supported within your custom error_report procedure.
3. The procedure error_report must be created within the object to handle the error.
Custom error handlers are created when you need to change the way errors are handled during a process. Usually you will turn the object that is running the process into its own error handler. When the process begins, you save the current value of error_object_id (usually to a property) and set the value of error_object_id to the current object. At this point, all errors will be directed to the current object. When the process is complete, error_object_id should be restored to its original value. This is typically done as follows:
// create property to keep track of previous error handler
Property integer OldErrorId 0
:
Procedure Run_Process
integer hID
Set OldErrorId to Error_object_id // remember old error object
Move Self to Error_object_id // make self the error object
// At this point, all errors will be directed to error_report
// within this object.
Repeat
//run the process
Until (done)
Get OldErrorId to hId
Move hId to Error_object_id // restore original error object
// At this point, all errors will again be directed to
// the previous (presumably standard) error handler
End_Procedure
While custom error handler error_report can be made to do anything you wish, you must provide logic to prevent recursive error reports (i.e., an error creating an error). You do this by creating a property named error_processing_state and setting this property to true while within the error_report procedure and resetting it to false when the procedure is complete. Because other objects may check the value of this property to see if an error is in process, this is a public property and must be provided in all error handlers. Here is a typical example of how this property is used:
// This property MUST be created within the object or the object's class.
Property Integer Error_Processing_State False
:
Procedure Error_Report Integer iErrNum Integer iErrLine String sErrText
// if we are already within an error, do nothing
If (Error_Processing_State(self)) ;
Procedure_Return
// mark that we are now processing an error
Set Error_Processing_State to True
// perform custom error handling
// mark that we are no longer processing an error
Set Error_Processing_State to False
End_Procedure
Often a custom error handler will want to augment the behavior of the standard error handler. For example, an error handler may wish to log an error and, if the error is of the right type, display a standard pop-up error dialog. Your custom error_report procedure does this by sending the message error_report to the standard error handler (usually the previous error handler). This is done as follows:
Procedure Error_Report Integer iErrNum Integer iErrLine String sErrText
Integer hId
If (Error_Processing_State(Self)) ;
Procedure_Return
Set Error_Processing_State to True
// perform custom error handling
// Now direct the error to the standard error handler
Get OldErrorId to hId
Send Error_Report of hId iErrNum iErrLine sErrText
Set Error_Processing_State to False
End_Procedure
More information is provided in the error_report procedure's documentation.
The Error_Report event gets called when an error is generated and this procedure may be used to customize error reporting. When customizing unhandled error reporting a special command, CallStackDump, is provided that allows you to retrieve the message stack information.
The CallStackDump command retrieves the same message stack information you would see in your error dialog and places it in a string. You can use this do other things with the error information such as logging it to a file or sending an error report. The CallStackDump command should only be used within the error handler's Error_Report event.
The following example creates a replacement error handler that logs all unhandled errors. Because this is an example, the logging will simply display the error in the showln dialog.
Object oError is a ErrorSystem
Property Boolean pbInError False
Procedure Error_Report Integer iError Integer iLine String sErrMsg
String sStack
Boolean bUnhandled bBusy
// augment to log unhandled errors
Get pbInError to bBusy
If not bBusy Begin
Set pbInError to True
Get IsUnhandledError iError to bUnhandled
If bUnhandled Begin
CallStackDump sStack
Send LogUnhandledError iError iLine sErrMsg sStack
End
Set pbInError to False
End
// now do the normal error report.
Forward Send Error_Report iError iLine sErrMsg
End_Procedure
Procedure LogUnhandledError Integer iError Integer iLine String sErrMsg String sStack
Showln "Unhandled Error: " iError " at " iLine
Showln sErrMsg
Showln sStack
Showln
End_Procedure
Move Self to Error_Object_Id
End_Object
See Also