Tuesday, January 16, 2007

Handling Errors in Visual FoxPro

There is no question that having a custom error handler working behind the scenes in your Visual FoxPro Application is not only a good thing, but essential for good, stable production systems. Oftentimes, these custom error handlers are nothing more than a generic message alerting the user that a problem has occurred. Good designs will then dump the error details to a file or table for later analysis. While this method is a good first step, so much more can be done. Consider the following:

1. Instead of a generic message, write code to work with certain predictable errors. In my code, I like to trap errors such as "E1, File Does not exist", "E6, Too many files open", "E1001, Feature is not available", "E2012 Cannot call SetFocus error", and "E2028 API call caused an exception". In these cases, special messages alert the user to what is going on, while code behind the scenes tries to handle the problem. For some errors, a simple RETRY or RETURN works well (like for error code 1001). For others, more work (error code 1) needs to be done.

2. Dumping the error contents to a file or table is a nice idea, but what if the user never tells you they received an error? Without you knowing, you can't fix it. In a perfect environment, you could email a copy of the error details to the development staff automatically through FoxPro. Messages can also be posted (via XML and web services) to an Internet support site. Simply including tech support's phone and fax number on the error message may give the operator a little more incentive as well. In the end, no method is foolproof. The important thing to remember is that no one likes an error, and more importantly, no one likes to get the same error over and over.

3. Intermingle all available error handling techniques in the application. This includes TRY...CATCH...FINALLY blocks where appropriate, using form and class level error methods, and even writing bullet-proof code (checking to make sure a table isn't already open before you USE it, for example). Spending time on this endeavors will pay dividends in the end. Good error handling doesn't go away, once it is in place it will continue to do its work throughout the life of the application.

4. Leave ON ERROR alone! There is sometimes a need to handle certain potential error cases differently. To achieve this, developers often swap ON ERROR routines. Take this typical case:

The global ON ERROR points to some code that displays a generic message when an error occurs. In a procedure, the developer needs to try to open a table for EXCLUSIVE use. So, the developer wraps the USE...EXCLUSIVE command around a customized ON ERROR that handles the error case when a file is in use. Next, the developer sets the ON ERROR command back to the global handler.

*-- Set up global error handler
ON ERROR oApp.gobal_err_hand(ERROR() ,;
LINENO(1) ,;
SYS(16) ,;
SYS(2018) )

*-- Start event processing (run the app!)

*-- function to open a table exclusively
FUNCTION open_data_exclusive()

LOCAL lcOldError
lcOldError = ON("ERROR")

ON ERROR MESSAGEBOX("Can't open the file!")
USE sometable IN 0 EXCLUSIVE
ON ERROR &lcOldError

RETURN USED('sometable')

This method has many flaws, but certainly gets the developer where he or she needs to be. But what if the developer forgets to switch the error handler back? What about SET COMPATIBLE? What if a different error occurs?). This method also begs the question: Why not build a single a method with a large CASE structure to handle the specific error conditions? Of course, this is the best approach. Use a single ON ERROR command at the start of the application, and then work with that handler. No switching allowed! Things are easier now in newer versions of FoxPro. TRY...CATCH...FINALLY essentially renders the ON ERROR swap totally and utterly archaic.

Another side-effect of using ON ERROR in this way is that if you're like me, then you have a debug menu and a debug environment that will treat errors differently. When an error occurs I don't want to get a generic message. I want the ability to suspend the application and start a debugging session. See my blog entry on the debug menu for more details!


E.R. Gilmore said...

Would it be possible for you to use a diffent font color for your code samples? The light gray is very hard to read..

Tod McKenna said...

Will do!

Hopefully the dark, dark red works better...