Handling Errors During Report Runs

Visual FoxPro 9.0 provides significant enhancements to the process of creating output, including new opportunities to invoke application code during the native Report System's internal data-handling and output-rendering procedures. To adequately provide users with the ability to develop such programs, Visual FoxPro 9.0 also enhances error-reporting and debugging during a report run.

This topic covers error-handling features with special significance for reporting, and examines different strategies for handling abnormal conclusions of report runs.

Note

Application components such as the Report Output Application and Report Preview Application partner with the Visual FoxPro Report System during output processing. They function as factories to provide appropriate object references to the Report Engine. Although these components may trigger errors and require debugging, they are not active during the internal report run, which spans processing events from the BeforeReport Event to the AfterReport Event. You can debug any issues with these components similarly to any other Visual FoxPro application; they are not covered by this topic. For more information about the sequence of events in a report run, see Understanding Visual FoxPro Object-Assisted Reporting.

Tip

You can use all standard Visual FoxPro debugging tools, such as the Trace Window, when examining your reporting code. You will also find the ReportListener Debug Foundation Class useful as you investigate report event sequences.

Strategies for error-handling during a report run

Because Visual FoxPro's output processing sequence can include extensive user code, you may need to end a report run before it finishes due to a code error. You may also want to end a report run prematurely because an end-user wants to interrupt the report. Additionally, an external error handler may detect a reason to end your application, while a report run is in process.

In all these situations, you can call the ReportListener CancelReport method to make sure Visual FoxPro does any special cleanup, such as closing a print queue, necessary to end the report, and your application, safely. See CancelReport Method for more information.

The ReportListener User Feedback Foundation Class provides a good model for canceling a report in response to an end-user request. See the DoMessage Method topic for the code in this class' CancelReport Method. Notice that the method issues a NODEFAULT if the end-user opts to resume running the report in response to a dialog box. Your code can follow a similar strategy in response to recoverable errors. For critical errors, you may prefer to end the report unilaterally, and end the application following report clean-up.

You can check to see whether the report ended normally using the SYS(2024) function. This function will return "Y" during a report run, through the conclusion of the Unload Event, if the report did not complete its run normally. At this point, the internal Visual FoxPro Report System processing has concluded. If you determine that the abnormal end to the report run did not occur as a response to an end-user's request, you can safely CANCEL or QUIT or request similar handling from an external application object. For more information, see Microsoft Visual FoxPro Technical Support.

Example 1: Cancelling a report

In the following example, an error occurs in a ReportListener's BeforeReport event, and its Error event responds by calling the class' CancelReport method. The UnLoadReport event code reports the value of SYS(2024) as "Y," as a result of the Error event's action.

Note

If the error occurred during the LoadReport event, SYS(2024) would not return "Y." No internal processing has occurred as yet, so there would be nothing for the Report Engine to clean up.

DEFINE CLASS errorListener as ReportListener
  PROCEDURE BeforeReport()
    REPORT FORM ? 
  ENDPROC
  PROCEDURE Error(nError, cMethod, nLine)
     WAIT WINDOW MESSAGE() 
     * MESSAGE() will report:
     * Report contains a nesting error.
     THIS.CancelReport()
  ENDPROC
  PROCEDURE UnloadReport()
     WAIT WINDOW SYS(2024)
     * SYS(2024) will report: "Y"
  ENDPROC
ENDDEFINE

Example 2: Ending an application as a result of report error

In the example above, the ReportListener-derived class handles the error. You may prefer to bubble the error event to an external application handler for determination of appropriate action. In this case, the external application needs to know that a report run was in progress when the error occurred, so it can cancel the report properly before ending the application.

To determine that a report run is in progress in external code, use the SYS(2040) function. For more information, see SYS(2040) - Detect Report Status.

The following example checks the return value of SYS(2040) in an ON ERROR Command handler.

PUBLIC oApp
oApp = CREATEOBJECT("MyApp")
ON ERROR oApp.ErrorHandler()
WAIT WINDOW "Some processing here..."
REPORT FORM ? OBJECT oApp.oE
WAIT WINDOW ;
  "Some more processing here, " + ;
  "will not occur because of error..."  

DEFINE CLASS MyApp AS Custom
   oE = NULL
   PROCEDURE Init
      THIS.oE = CREATEOBJECT("errorListener")
   ENDPROC
   PROCEDURE ErrorHandler
     MESSAGEBOX("Error! " + MESSAGE())   
     IF SYS(2040) # "0"
       THIS.oE.cancelReport()
     ENDIF
    IF _VFP.StartMode = 0
       CANCEL
    ELSE
       QUIT
    ENDIF
  ENDPROC
ENDDEFINE    

DEFINE CLASS errorListener as ReportListener
  PROCEDURE BeforeBand()
    * parameters are missing
  ENDPROC
  PROCEDURE CancelReport()
    DODEFAULT()
    MESSAGEBOX("Cancelling Report!")
  ENDPROC
ENDDEFINE

Example 3: Canceling a report during application shutdown

You can use a similar strategy in an ON SHUTDOWN handler if it is possible for this handler to be called during a report run. The ON SHUTDOWN handler needs a reference to the appropriate ReportListener object, just as the application object in the example above does. For more information, see ON SHUTDOWN Command.

Tip

Timers do not fire during a report or label run. However, it is often possible for end-users to decide to close your application while Visual FoxPro is generating output. Therefore, do not assume that the need to cancel a report is always associated with an error.

You can use the following simple example to verify this capability.

PUBLIC rl
ON SHUTDOWN DO MyShutdown

rl = CREATEOBJECT("reportListener")
REPORT FORM ? OBJECT rl  && use a long report!
* use the close box on the main Visual FoxPro
* window while the report runs.

PROCEDURE MyShutDown
  IF SYS(2040) # "0"
     MESSAGEBOX("Now Cancelling Report!")
     rl.CancelReport()
  ENDIF
  ON SHUTDOWN
  IF _VFP.StartMode = 0
     CANCEL
  ELSE
     QUIT
  ENDIF
ENDPROC

Example 4: Using a ReportListener's Error event

In two of the three examples above, the ReportListener class does not have Error event code. If you include Error event-handling in a ReportListener class, as shown in the first example, this code can decide to handle, or bubble, any error it receives.

If the Error event handles an error, it can safely use CANCEL or QUIT. You can use these commands in other ReportListener events or methods as well. Be sure to invoke the ReportListener's CancelReport() method before issuing the CANCEL or QUIT statement, however.

In the next example, the ReportListener class uses its Error event to handle the error, and then ends the report immediately.

Tip

The ReportListener's Error event will not handle errors that occur during a report run but outside the ReportListener's own code, such as an error in a user-defined function (UDF) within the report or an error in a report expression. For this reason, having ReportListener Error event code does not free your external error handler of the responsibility to be aware of reporting errors. A combination of approaches is necessary to handle all types of errors that may occur in the course of a report run.

LOCAL oE
oE = CREATEOBJECT("errorListener")
WAIT WINDOW "Some processing here..."
REPORT FORM ? OBJECT oE
WAIT WINDOW ;
  "Some more processing here, " + ;
  "will not occur because of error..."  
  
DEFINE CLASS errorListener as ReportListener
  PROCEDURE BeforeBand()
    * parameters are missing
  ENDPROC
  PROCEDURE Error(nError, cMethod, nLine)
   WAIT WINDOW "Error occured! " + CHR(13) + ;
               "SYS(2024) before CancelReport: " + SYS(2024) 
   THIS.CancelReport()  
   CANCEL
  ENDPROC
  PROCEDURE CancelReport()
    IF SYS(2040) # "0"
       MESSAGEBOX("Cancelling Report!") 
       DODEFAULT()
       WAIT WINDOW "SYS(2024) is now: " + SYS(2024) 
    ENDIF
  ENDPROC
ENDDEFINE

Example 5: Using structured error handling with reports

As a final strategy, you can use structured error handling with reports. When you run the sample code below, however, notice that your code does not get to the CATCH construct until after the report run has ended, from the Report System's perspective. Although you can use the CancelReport method, the user code you run in this method should be designed to run safely whether or not a report is actually in progress.

LOCAL oE

TRY
  oE = CREATEOBJECT("errorListener")
  WAIT WINDOW "Some processing here..."
  REPORT FORM ? OBJECT oE
  WAIT WINDOW ;
  "Some more processing here, " + ;
  "will not occur because of error..."  
CATCH WHEN .T.
   WAIT WINDOW "An error occured! " + CHR(13) + ;
               "SYS(2024)=" + SYS(2024) + CHR(13) + ;
               "SYS(2040)=" + SYS(2040)
   * SYS(2024) will report "N"
   * SYS(2040) will report "0"
   oE.CancelReport()
ENDTRY
   
DEFINE CLASS errorListener as ReportListener
  PROCEDURE BeforeBand()
    * parameters are missing
  ENDPROC
  PROCEDURE CancelReport()
    IF SYS(2040) = "0"
       MESSAGEBOX("No report in progress, " + CHR(13) + ;
                  "but this method can still do user cleanup!")
       NODEFAULT
    ELSE
       MESSAGEBOX("Cancelling Report!") 
       DODEFAULT()
    ENDIF
  ENDPROC
ENDDEFINE

Errors during a report run that do not occur in lines of code

In Visual FoxPro 9.0 and all earlier versions, it is possible for errors to occur during report runs that are not specifically associated with a traceable line of user code. Such errors include report expressions or report variables that the Report Engine cannot resolve, as well as difficulties initializing the report's Data Environment.

Visual FoxPro 9.0 maintains backward-compatibility with its handling of such errors, as described in this section.

When an error occurs outside user code, Visual FoxPro's internal error handling does not offer you an opportunity to suspend the report, because there is no line of code on which to suspend.

If you do not have an ON ERROR handler or a TRY… ENDTRY construct in place, Visual FoxPro does not invoke your error handling until after it does its internal cleanup and ends the report run.

As demonstrated in the examples in this topic, the value of the MESSAGE() function is available if it occurs in ReportListener code or other user code you invoke during a report run. However, if the error is not associated with a line of code, MESSAGE() is not assigned until after the Report Engine concludes its cleanup.

If an error is not associated with a line of code, the value of MESSAGE() is not assigned at all unless you have some form of error-handling in place at the time the REPORT FORM or LABEL command occurs. If you do not have an error handler, and if you have used the CLEAR ERROR command before the report run, MESSAGE() is still empty after the report run. If you have not used CLEAR ERROR, MESSAGE() retains its previous value.

Commands not supported during a report run

To error-handle well, you should start by planning to use only commands that are safe to use during a report run.

The Visual FoxPro commands in the table below are all unsupported during internal output processing, between the BeforeReport event and AfterReport event (inclusive).

Not all the errors in this table will generate an immediate error, but use of these commands during output processing leads to unpredictable results.

These commands are unsupported whether they occur:

  • Directly in ReportListener event code.

  • In additional code invoked by a ReportListener, such as code in a procedure library.

  • In the command window while execution is suspended.

  • Using code invoked directly in report or label expressions, such as user-defined functions (UDFs).

In some cases, as indicated in the table, the commands are also not supported in the ReportListener's LoadReport Event and UnloadReport Event. These two framing events provide user hooks at each end of the internal processing run.

As a final, exceptional case, while the OutputPage Method may be running after the report run concludes, if called by a PreviewContainer object, this method may need access to resources built into the application, such as image files, to perform its task. For this reason, using commands such as CLEAR ALL that might unload the calling application from memory are not supported until the preview concludes. When the REPORT FORM or LABEL command includes the NOWAIT keyword, the preview concludes at the time the preview window closes, unless the user chooses to print from preview. If the user chooses to print, the preview concludes at the time Visual FoxPro finishes sending the report output to the printer.

Note

In addition to the commands listed below, all commands that change sessions, work areas, record pointers, or other attributes of the data on which the REPORT FORM and LABEL commands act, should be used with caution. As in previous versions of Visual FoxPro, it is the responsibility of the user to restore the state of the data appropriately when using these commands, with the understanding that other ReportListeners and additional user code, as well as the internal Report Engine, may be affected by their changes. For example, although the BROWSE command does not appear in this table, it has potential to change the record pointer, trigger additional code attached through user-defined functions (UDFs), and affect data relationships, so it should be used with caution.

Command(s) Supported in LoadReport and UnloadReportevents? Remarks

CLEAR ALL

RELEASE ALL

CLOSE ALL

No

This restriction is primarily to preserve the ReportListener reference and data tables as expected by the Report Engine during its processing of the report scope.

RETRY

RETURN TO MASTER

RETURN TO<Procedure Name>

No

 

?, ??, ???

\, \

@... SAY

EJECT, EJECT PAGE

PRINTJOB… ENDPRINTJOB

LIST, DISPLAY, TYPE

SET DEVICE,PRINTER

Yes

These output commands are supported during the output run if the target device for the output run is different from the target device for the output command. For example, if your REPORT FORM command has a PREVIEW clause, you can LIST MEMO TO<filename> during the report run.

BEGIN TRANSACTION and additional transaction-related commands

Yes

Transaction commands are supported if the transaction is concluded within one invoked procedure or one ReportListener event.

MODIFY/CREATE CLASS, FORM, LABEL, REPORT […]

Yes

All commands that invoke interactive Visual FoxPro Designers are unsupported, whether modal or invoked with the NOWAIT keyword. You can invoke a text editor (MODIFY FILE or COMMAND).

REPORT FORM

LABEL

No

REPORT FORM and LABEL commands cannot be nested.

Errors associated with the Visual FoxPro Report System

The following table provides a list of errors that can occur for reasons specific to the run-time processing of reports and labels.

Error message and number Cause or scenario

"name" is not an object (Error 1924)

The Engine received a REPORT FORM Command or LABEL Command with an OBJECT <ref> clause and <ref> was not an object.

Class definition "name" is not found (Error 1733)

The Engine received a NULL reference as the result of an OBJECT TYPE<N> clause on a REPORT FORM or LABEL command, indicating that the application specified in the _REPORTOUTPUT system variable could not resolve this value to a ReportListener Object object reference.

Data type is invalid for this property (Error 1732)

The Engine an OBJECT TYPE<N> clause on a REPORT FORM or LABEL command, and the <N> parameter was not of numeric type. This error may also occur in connection with use of the OutputPage method, discussed below.

Report contains a nesting error (Error 1645)

A REPORT FORM or LABEL command was invoked during another report or label run. Note that this error still occurs if the nested command uses backward-compatible report mode, if a different report is requested, or if the nested command includes a reference to a different ReportListener object.

Syntax error (Error 10)

SET REPORTBEHAVIOR Command was issued with an incorrect parameter.

The current object does not inherit from class "name" (Error 1935)

The Engine received an object reference that does not inherit from the Visual FoxPro ReportListener base class.

Variable "variable" is not found (Error 12)

The Engine received an OBJECT TYPE<N> clause on a REPORT FORM or LABEL command, and the application specified in the _REPORTOUTPUT system variable could not be found or the variable was empty.

Must specify additional parameters (Error 94)

Function argument value, type, or count is invalid (Error 11)

Data type is invalid for this property (Error 1732)

Error writing to file "file" (Error 1105)

Output page "page" is not available (Error 2194)

The errors in this group all indicate that the ReportListener's OutputPage method was invoked incorrectly. For more information, see OutputPage Method.

See Also

Reference

TRY...CATCH...FINALLY Command
Debugging and Error-Handling Language
Error Event (Visual FoxPro)
CANCEL Command
QUIT Command
MESSAGE( ) Function
CLEAR Commands

Concepts

Structured Error Handling
Class and Object Error Handling
Handling Run-Time Errors
Error Handler Priority