Sunday, November 01, 2009

Utility Programs, Part II - Error handlers

Error Handler

Originally, none of the programs had an error handler. I had never used an error handler, and none of the preexisting programs had one. I tried to write the programs to eliminate as many errors as I could. This was not enough to totally prevent errors, and so I eventually began to explore setting up an error handling system.

The error handler was originally centered on handling file-does-not-exist problems (if the requested file is on an unavailable file server, this can easily cause the error) and file-is-in-use-by-another errors, and had minimal processing for other errors. Except for printer-not-ready errors, most errors resulted in shutting down the program, since the consequences of allowing the program to continue with an unresolved error is great. Exceptions were allowed if the section of the program from whence the error originated indicated that it would be able to handle the problem.

For instance, if a table was unavailable and the user did not want to retry, and the section of the program where the error occurred had previously set variables indicating that the command was being checked, the error handler would return control to that portion of the program after setting a variable to an error value and the program would see that an error had occurred and cancel the operation.

What would happen if the program had simply continued, thinking the table had been opened? The user would eventually be presented with some other error. Perhaps a field would be referenced and a variable-not-found error would occur, or a string of such errors if the error handler kept returning control to the program. Eventually, a command would probably be encountered which would cause FoxPro to believe that a table should be open. At that point, FoxPro would present the user with a list of available tables to open. Even if the user knew which table to select, any associated indexes would not be opened and so the indexes would be corrupted if the table was updated. No guarantee exists that the table would be positioned on the correct record or that the correct table would be chosen. If the wrong table was chosen, the program might proceed without further activating the error handler, as some commands are sufficiently general as to work with any table, but the wrong table would be used. What if the command was to update a record, delete a record, or to delete ALL records?

Even a variable-not-found error can be dangerous. If the variable not found was being used to initialize another variable, then the variable is not initialized and may itself not be found when the variable is later referenced. {If the variable has been declared in some fashion, the variable will exist, but may be of the wrong data type.) If either variable is used in another command, then that command will fail, whatever that command may be. For an extreme example, what if table work area numbers are kept in variables, and the program intends to switch work areas, delete all the records in a temporary table, and then return to the original work area, which holds a permanent table with 30,000 records? If the work area variable does not exist, an error will occur and the work area selected will remain that holding the permanent table. If the program is allowed to continue, and the next command is to delete all records, the 30,000 records in the permanent table will be deleted instead of the records in the temporary table.

It would be best if some means were provided to allow the user to safely continue. Eventually, I devised a system where variables held the name of the module to return to when an error occurred. If the error was a printer error, a special variable was referenced which was set to the name of the report menu module. If a table was being opened and if the operation was being checked, the error handler returned control to the program at the point where the error occurred. Otherwise, and for almost all other errors, the error handler attempted a return to the designated main module. If a special variable was set with the name of the main module, the program tree was checked to see if the name was present. If the name was not found, an alternate possible name, such as MainMenu was checked. If nothing was found, a RETURN TO MASTER command was given. Note that such a command will result in leaving the current program if the program was called from another program. In some cases, and for some errors, the situation was deemed sufficiently serious to directly quit FoxPro. Tables were always closed in situations where control was not returned to the area of the program where the error occurred. If the error occurred while the user was in the designated top module, the user might be returned to the place of the error. If the error occurred during initialization of the program, further errors might occur even (or maybe especially) if the user is returned directly to the top module. Though such situations are rare, I had the error handler place a counter in a public variable the error handler itself created, and if multiple errors of the same general type were occurring too close together, the error handler would decide that the situation couldn't be saved and simply quit FoxPro.

Almost all errors resulted in the user being given notification. Sometimes the error handler message was suppressed to allow the module where the error occurred to give a message, and sometimes custom messages where passed to the error handler. More often, in cases where a FoxPro type error occurred, the error handler presented the FoxPro error message and number.

Non-FoxPro errors and events could also occur, as the error handling portion could be bypassed and a call made directly to a section of the error handler that saved the errors. Sometimes, specific areas of programs were set to log unusual or disturbing events or activities, in which case the error would be saved as a custom error message and number (usually, but not always, zero), along with the user name, the name of the module (or a special name), and the date. These records would go into the same table as the FoxPro error records. Additionally, the error handler recorded the frequency of the error by looking backward through the file for other errors with the same message, number, user name, module name, and date, and then incrementing the error count and saving the result.

In the end, I did not record large numbers of errors, except in unusual situations.

Startup Error Handler

One type of error that disturbed me was an error that seemed unreachable. Some users loaded FoxPro from their file server and then received a file-does-not-exist error when FoxPro attempted to load the application, because the application was on a server to which they had failed to attach. This happened relatively frequently, as some users failed to provide the proper password when the attempt to attach was made. In some cases the subsequent attempt to map a drive also failed. In some cases, the user did not know the right password, because the user had changed the password on the default server and not on the other servers. This was all occurring in DOS, sometimes even on Windows computers running DOS windows (the programs were in DOS). When such an error occurred, the error handler was not activated because the application could not even be found. FoxPro presented its own error message through its native error handler. After acknowledging the message, the user was left in the command window. The user was frequently trapped in FoxPro, not knowing how to exit.

Eventually, I decided to do something about it. FoxPro had a startup program that installed the Run menu. I didn't use the Run menu. I substituted my own program for the startup menu. The new program ran a version of my error handler in a subdirectory I created off the FoxPro directory, and specified a table in that subdirectory when errors were logged. I gave users Novell write rights to that directory, to allow them to write to the table. The new system worked very well, enabling me to identify users with password problems and inform the LAN Administrator, frequently before the users themselves did (when the users did complain, they frequently blamed the program or some unknown problem, rather than the password).

Error Handler for Visual FoxPro

In converting the error handler to Visual FoxPro, I made two versions: one as a program and one as a class. Although a class was needed to fully handle errors occurring in forms and classes, a separate error handler was also needed for those cases where the class error handler was not available. The updates from the earlier error handler involved mainly adding error checks for new error types, saving class information in the tree of procedure names leading to the module in which the error occurred, modifying the file structure of the error log table, and using the Windows msgbox instead of coding message windows.

Note that error handling within classes is more complicated than simply installing a global error handler, as ideally the class should have some native error/exception handling mechanisms built-in, with the error passed to the global error handler only if it falls outside of what the class can handle. Since I wanted errors recorded, I made the error log portion available separately from the error handler, so the class error handler could log the errors without calling the global error handler.

My Time at Geocities
My Geocities Homepage
My Geocities Guestbook
Program List
Utility Programs, Part I - Printers
Utility Programs, Part II - Error handlers
Programs, Part I
Programs, Part II
Programs, Part III
Programs, Part IV

Labels: , ,


Post a Comment

<< Home

Newer Posts . . . . Older Posts