Class: ErrorSystem [Obsolete]

Properties  Events  Methods    Index of Classes

Supports DataFlex's interactive interface for all errors generated in a program

Hierarchy

cObject
---cBaseErrorHandler
------cWindowsErrorHandler
---------ErrorSystem

Library: Windows Application Class Library

Package: Dferror.pkg

Description

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.

Usage

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.


Note

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.


Understanding How Error Handling works

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.


User Errors and Unhandled Errors

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.

Generating Errors

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.

Recommendations



Compatibility Note

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.



Trapping and Ignoring Errors

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.

Creating Your Own Error Handler

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.


Directing and Redirecting Errors

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



Protecting against Error Recursion

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


Augmenting the Standard Error_Report Behavior

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.

Customizing Unhandled Error Reporting

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

UserError