Compartilhar via


Error Handling In VBScript, Part Two

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.

Comments

  • Anonymous
    August 23, 2004
    The comment has been removed
  • Anonymous
    August 23, 2004
    The comment has been removed
  • Anonymous
    August 24, 2004
    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.
  • Anonymous
    August 27, 2004
    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. ^_^
  • Anonymous
    August 27, 2004
    The comment has been removed
  • Anonymous
    September 02, 2004
    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?
  • Anonymous
    September 02, 2004
    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.