Here's how you proceed. When your most local (immediate) error trap gets hit, make sure you clean up as required locally first. For example, make sure you close any files that you opened in this routine. Once that's done, and if this routine is not an event handler, reraise the error (in reality, you might raise some other error here) and repeat this process for each previous stack frame (a stack frame refers to an entry in the call chain); that is, continue this process for each preceding call until you get back up to an event handler. If you've cleaned up locally all the way through the call chain and if you had an error handler for each stack frame (so that you didn't jump over some routines), you should now have effectively rolled back from the error. It will seem as though the error never really happened. Note that by not reporting errors from anywhere other than an event handler, you will not have shown your user a stream of message boxes.
Localized error handling might need error handling itself. Look at the following code fragment:
On Error GoTo Error_Handler: Dim nFile As Integer nFile = FreeFile Open "c:\time.txt" For Output Access Write As nFile Print #nFile, Time$ Close nFile Exit Sub Error_Handler: ' Roll back! Close nFile Exit Sub
Imagine you have opened a file and are attempting to roll back in your error handler. How do you know whether or not you opened the file? In other words, did the error occur before or after the line of code that opens the file? If you attempt to close the file and it's not open, you'll cause an error-but if it's open, you don't want to leave it open as you're trying to roll back! I guess you could use Erl to determine where your code erred, but this implies that you're editing line numbered source code-yuck. (You'll recall from Tip 2 that we added line numbers only to the code for the final EXE, not to the code we're still editing.) Probably the best way to determine what did or did not get done is to limit the possibilities; that is, keep your routines small (so that you have only a small problem domain). Of course, that's not going to help us here. What we need to do is apply a little investigation!
Given this type of problem, you're probably going to have to test the file handle to see whether it points to an open file. In the code above, we would probably use FileAttr(nFile, 1) to determine whether or not the file nFile is open for writing. If the file is not open, FileAttr raises an exception (of course). And obviously, you can't handle this locally because you can't set an error trap from within an error trap unless your error handling is in another routine! (Refer to Tip 5 for details.)