Error Handling In VBScript, Part Two

Error Handling In VBScript, Part Two

  • Comments 7

The way VBScript implements the error semantics I mentioned last time is sort of interesting.  Today I'll talk a bit about the implementation details, and then next time I'll finish up by talking about some philosophy of error handling.

Before I go on, if you haven't already, read my recent post Spot The Defect to see how IDispatch returns error information in an EXCEPINFO.

OK, so we know that when an error occurs we are going to have at the very least an HRESULT, and possibly some additional information -- strings describing the error, and so on.  When VBScript's IDispatch caller gets an error it records a copy of the error in an internal buffer and returns a special error code, SCRIPT_E_RECORDED.  That way the script engine knows to not attempt to record the error again, but rather to look at the recorded buffer state when it needs information about the real error.  Script engine users should never see this error number.  You'll only see it if you're watching EAX in the debugger during a script error.

 Note that both On Error statements clear the recorded error information buffer.

Once the error winds its way back to the interpreter, the interpreter does a long jump to an error handling routine which saves off additional information that the interpreter knows.  For instance, what is the name of the last-accessed variable -- because some error messages include information like that.  The interpreter then checks to see if we're in 'resume next' mode, in which case it cleans up the stack, moves the instruction pointer to the next beginning-of-statement marker, and restarts the interpreter.

There is some additional goo that happens in here to make script debugging scenarios work.  I won't go into that -- a discussion of all the ways that script errors and the debugger interact would be lengthy indeed.  There is also code to handle weird scenarios -- like, a JScript block with a try-catch calls a VBScript block which calls a JScript block, which throws an exception -- but I won't go into those either.

If the engine is not in 'resume next' mode then it reports the error to the host, and returns another special code, SCRIPT_E_REPORTED.  This error indicates to the host that an error occurred but that the host already knows about it.  This is intended to forestall this problem:  suppose you have a JScript block that calls a VBScript block which causes an unhandled error.  Both engines are going to see an error code.  The JScript engine does not know that it called the VBScript engine, and hence that the host already knows about the error.  The JScript engine just knows that it made a call and an error code came back.  The VBScript engine needs some way to tell the JScript engine to treat this like an error, but also make sure that JScript does not tell the host to pop up another dialog box saying that there was a script error.  Without such a mechanism, you can construct scenarios in which the same error is reported to the user an arbitrarily large number of times.

The implementation of the Err object should now be transparent -- it simply looks at the information which the engine recorded at the time of the error.  The error number, description, and so on, are reported as they were reported to us by the object which failed.  If there was no such information reported, well, you're out of luck then.

  • > Note that both On Error statements clear the
    > recorded error information buffer.

    This seems to be a big difference between VBScript and VB. I had developed the style of putting an On Error Goto handler_label before something whose failure I was prepared for, and two On Error Goto 0 statements, one just after the something and one at the beginning of the handler.

    But then for one VB application it made sense to have three of these in one function, and after handling and recovering from the first failure, the second failure aborted the function instead of getting sent to its handler. I thought this was a VB bug but I was wrong. Someone taught me that in VB the Resume statement must be executed IN ADDITION TO the On Error Goto 0 statement. The Resume statement told the VB runtime to consider that the function had recovered from the failure (even though the function was still executing in the handler). Then the second failure went to the second failure's handler.

    Suppose the program is being done in VBScript instead of VB, and suppose the developer can still anticipate some failures and wants to handle them and continue. On Error Goto 0 is still a fine method of letting unexpected (unhandled) failures propagate upwards. But if VBScript has no On Error Goto some_handler, and no need for a Resume statement, what is the usual method of handling an anticipated exception in VBScript?
  • Norman,

    The usual method [for VBScript] is by using On Error Resume Next and checking error codes after the statement executes.

    As for why the [VB] function didn't raise out of your error handler, once VB's handler is triggered, it's considered to be in handling mode until the function ends or a Resume statement is encountered. If errors are expected in the error handler (and execution should not return back into the function body for the initial error), the handler should first issue a Resume to a label inside the error handler. Then the On Error GoTo 0 will work as expected.
  • 8/24/2004 5:34 AM Ron

    > The usual method [for VBScript] is by using
    > On Error Resume Next and checking error
    > codes after the statement executes.

    This must make some coding structures even more painful in VBScript than in VB. Thank you for the information though.

    > As for why the [VB] function didn't raise
    > out of your error handler

    Right, I said someone taught me that the Resume statement must be used IN ADDITION TO the On Error Goto 0 statement.

    By the way errors were not expected in the error handler (this is the reason I put an On Error Goto 0 at the beginning of the error handler). But the handler Gotoed back to the normal chain of processing, and certain other possible errors were prepared for later (On Error Goto other_handler), but I didn't know at the time that the VB runtime still considered the function to be in handling mode and considered the new error a double fault. So yes I put a Resume in the handler just after the handler's On Error Goto 0. 3 labels and visible gotos per handler.
  • Curious about something. Does this mean that Resume Next is actually much slower than catching errors, because it terminates and restarts the interpreter every time? Or does this always happen, or did I just read that wrong?

    A side note: Goto sounds like a really messy brand of toy goop. It must be Friday. ^_^
  • First off, why the heck do you care how slow the error handler is? Error conditions are almost never due to something fast, and are frequently followed up by showing an error message to the user. Does it really matter whether it takes 30 ms or 40 ms to put up the "something failed" message box?

    Second, no, you're misunderstanding something. The interpreter is just a loop that fetches the next opcode and interprets it. Resume Next saves away error information, moves the instruction pointer forwards to the next beginning-of-statement, and does a goto to the top of the interpreter loop. Restarting the interpreter takes a couple billionths of a second. Saving away the error strings for later is the expensive part. (And that is under some circumstances deferrable, so you don't even incur that cost, but that is a topic for another day.)

    Not that it matters, but FWIW, catching errors a la jscript on the other hand is much slower. Searching the call stack for exception handlers, then doing all the cleanup of the various stack frames, branching to the exception handler, and doing all the bookkeeping necessary to maintain integrity in situations where people do stupid things (like put an "eval('return;');" inside a finally -- ouch!) is much more complex and therefore slower than just resetting the instruction counter and continuing merrily along.
  • How does IErrorInfo relate to EXCEPINFO? I have used IErrorInfo extensively in ATL COM objects to return error descriptions and source information back to VBScript, which seems to be what the EXCEPINFO structure does too? Are these two ways to do the same thing?
  • The point of IErrorInfo is to provide the information in an EXCEPINFO to objects that are designed to be called on vtable interfaces rather than via IDispatch.

    VBScript doesn't use IErrorInfo because VBScirpt never calls via anything but IDispatch. (OK, that's a lie; there is ONE special case which I will blog about at some point where we call IErrorInfo.)

    IErrorInfo is clearly the more "COM-like" way to do things. Many problems -- like the ones I mention Spot The Defect Part Two -- could have been avoided had the OLE Automation designers chosen to pass back error information via an interface rather than a struct.

    Unfortunately the OLE Automation interfaces were written very early in the history of COM, back when it really wasn't so clear what exactly "COM-like" meant. From a design perspective, lots of OLE Automation is essentially just a thin COM wrapper overtop of a Win32-API-style-hey-let's-pass-some-pointers-to-structs-around model. EXCEPINFO is part of that. Were we to design late-bound dispatch interfaces in COM today, they'd use interfaces like IErrorInfo. But, sadly, we're stuck with two ways to do the same thing.
Page 1 of 1 (7 items)