Error Handling in VBScript – Part II

Interpreting the Error Code and Description

Let’s look in a bit more detail at how the interpreter within the VBscript engine handles errors and how they are recorded in Err. Errors can be raised by the interpreter itself (and examples of these are the Subscript out of range and Division by zero errors above), but the main power of scripting is its use as an integration vehicle for COM components such as ASDI, and many of the errors that need to be handled will originate in such components. When an error occurs the interpreter is going to get an HRESULT, and possibly some additional context information — strings describing the error, and so on, and as it uses the standard programmatic interface to COM by calling IDispatch and therefore errors are returned in an EXCEPINFO structure. (This design dates back to the earliest days of COM; sorry.)

Making sense of those error numbers requires some delving into the depths of how COM represents errors — the HRESULT, which is a 32 bit unsigned integer where the high bit indicates whether it is an error or a success. The remaining bits in the high word indicate the “facility” of the error — into what broad category does this error fall? The low word indicates the specific error for that facility. HRESULTS are therefore usually talked about in hex, as the bit structure is a lot easier to read in hex! Consider 0x80070013, for example. The high bit is set, so this is an error. The facility code is 7 and the error code is 0x0013 = 19 in decimal. Some common facility codes are listed in Table 1 and Table 2 below.

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 these, nor the detailed error tracking you’d observe if you were debugging the COM object itself — say through the Visual Studio debugger.

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, assuming that the object returned error status. However for some common errors, when VBScript gets the error numbers back from calls to IDispatch objects, it replaces them with the equivalent VBScript error number. I have never particularly liked this feature, but VB6 does it, so we’re stuck with it for backwards compatibility reasons. This can be a little confusing if you’re debugging a problem — you see one error go out of the object, but a different error is reported to the host. The mapping table that VBScript uses is below at Table 1. (Note that there seems little logical structure to these codes, so don’t look for one. They seem to have been assigned for largely historic reasons, to the extent that the error codes listed in blue are the same as Altair BASIC which Microsoft’s first product, written by Bill Gates and Paul Allen back in 1975 for the Altair 8080!)

Table 1 — VBscript Errors Codes and Facility Mappings

No Description Facility Code Facility Name
5 Invalid procedure call or argument 0x80070057 E_INVALIDARG
6 Overflow 0x8002000A DISP_E_OVERFLOW
7 Out of memory 0x80030008 STG_E_INSUFFICIENTMEMORY
0x8007000E E_OUTOFMEMORY
0x8002000B DISP_E_BADINDEX
9 Subscript out of range 0x80028CA1 TYPE_E_OUTOFBOUNDS
10 This array is fixed or temporarily locked 0x8002000D DISP_E_ARRAYISLOCKED
11 Division by zero n/a
13 Type mismatch 0x80020005 DISP_E_TYPEMISMATCH
0x80028CA0 TYPE_E_TYPEMISMATCH
14 Out of string space 0x80029C4A TYPE_E_CANTLOADLIBRARY
17 Can’t perform requested operation n/a
28 Out of stack space n/a
35 Sub or Function not defined n/a
48 Error in loading DLL n/a
51 Internal error n/a
52 Bad file name or number n/a
53 File not found n/a
54 Bad file mode n/a
55 File already open
57 Device I/O error 0x80028CA2 TYPE_E_IOERROR
0x8003001D STG_E_WRITEFAULT
0x8003001E STG_E_READFAULT
0x80030103 STG_E_CANTSAVE
58 File already exists 0x80030050 STG_E_FILEALREADYEXISTS
61 Disk full 0x80030070 STG_E_MEDIUMFULL
62 Input past end of file n/a
67 Too many files 0x80030004 STG_E_TOOMANYOPENFILES
0x80030012 STG_E_NOMOREFILES
68 Device unavailable n/a
70 Permission denied 0x80030005 STG_E_ACCESSDENIED
0x80030013 STG_E_DISKISWRITEPROTECTED
0x80030021 STG_E_LOCKVIOLATION
0x80030100 STG_E_INUSE
0x80030101 STG_E_NOTCURRENT
0x80070005 E_ACCESSDENIED
71 Disk not ready n/a
74 Can’t rename with different drive n/a
75 Path/File access error 0x80030020 STG_E_SHAREVIOLATION
76 Path not found 0x80030003 STG_E_PATHNOTFOUND
91 Object variable not set n/a
92 For loop not initialized n/a
94 Invalid use of Null n/a
322 Can’t create necessary temporary file 0x80028CA3 TYPE_E_CANTCREATETMPFILE
424 Object required 0x80040154 REGDB_E_CLASSNOTREG
429 ActiveX component can’t create object 0x800401E3 MK_E_UNAVAILABLE
0x800401F3 CO_E_CLASSSTRING
0x800401F5 CO_E_APPNOTFOUND
0x800401FE CO_E_APPDIDNTREG
0x80080005 CO_E_SERVER_EXEC_FAILURE
430 Class doesn’t support Automation 0x80004002 E_NOINTERFACE
432 File name or class name not found during Automation operation 0x80030002 STG_E_FILENOTFOUND
0x800300FC STG_E_INVALIDNAME
0x800401E6 MK_E_INVALIDEXTENSION
0x800401EA MK_E_CANTOPENFILE
438 Object doesn’t support this property or method 0x80020001 DISP_E_UNKNOWNINTERFACE
0x80020003 DISP_E_MEMBERNOTFOUND
0x80020006 DISP_E_UNKNOWNNAME
440 Automation error 0x80004001 E_NOTIMPL
445 Object doesn’t support this action n/a
446 Object doesn’t support named arguments 0x80020007 DISP_E_NONAMEDARGS
447 Object doesn’t support current locale setting 0x8002000C DISP_E_UNKNOWNLCID
448 Named argument not found 0x80020004 DISP_E_PARAMNOTFOUND
449 Argument not optional 0x8002000F DISP_E_PARAMNOTOPTIONAL
450 Wrong number of arguments or invalid property assigned 0x8002000E DISP_E_BADPARAMCOUNT
451 Object not a collection 0x80020011 DISP_E_NOTACOLLECTION
453 Specified DLL function not found 0x8002802F TYPE_E_DLLFUNCTIONNOTFOUND
455 Code resource lock error n/a
457 This key is already associated with an element of this collection n/a
458 Variable uses an Automation type not supported in VBScript 0x80020008 DISP_E_BADVARTYPE
462 The remote server machine does not exist or is unavailable 0x800706BA RPC_S_SERVICE_UNAVAILABLE
481 Invalid picture n/a
500 Variable is undefined n/a
501 Illegal assignment n/a
502 Object not safe for scripting n/a
503 Object not safe for initializing n/a
504 Object not safe for creating n/a
505 Invalid or unqualified reference n/a
506 Class not defined n/a
507 An exception occurred n/a
32811 Element not found n/a
32812 The specified date is not available in the current locale’s calendar n/a

 

Table 2 — Standard Facility Codes

Symbolic Facility Code Value
FACILITY_NULL 0
FACILITY_RPC 1
FACILITY_DISPATCH 2
FACILITY_STORAGE 3
FACILITY_ITF 4
FACILITY_WIN32 7
FACILITY_WINDOWS 8
FACILITY_SECURITY 9
FACILITY_CONTROL 10
FACILITY_CERT 11
FACILITY_INTERNET 12
FACILITY_MEDIASERVER 13
FACILITY_MSMQ 14
FACILITY_SETUPAPI 15
FACILITY_SCARD 16
FACILITY_COMPLUS 17
FACILITY_AAF 18
FACILITY_URT 19
FACILITY_ACS 20
FACILITY_DPLAY 21
FACILITY_UMI 22
FACILITY_SXS 23
FACILITY_WINDOWS_CE 24
FACILITY_HTTP 25
FACILITY_BACKGROUNDCOPY 32
FACILITY_CONFIGURATION 33
FACILITY_STATE_MANAGEMENT 34
FACILITY_METADIRECTORY 35

 

Table 3 — Some Other Common Errors

Facility Code Facility Name Description
0x8000FFFF E_UNEXPECTED “Catastrophic failure” — something completely unexpected has happened.
0x80004001 E_NOTIMPL “Not implemented” — the developer never got around to writing the method you just called!
0x8007000E E_OUTOFMEMORY Pretty obvious what happened here
0x80070057 E_INVALIDARG You passed a bad argument to a method
0x80004002 E_NOINTERFACE COM is asking an object for an interface. This can happen if you try to script an object that doesn’t support IDispatch.
0x80004004 E_ABORT Whatever you were doing was terminated
0x80004005 E_FAIL Something failed and we don’t know what.
0x86664004 SCRIPT_E_RECORDED

 

 

 

This is how we internally track whether the details of an error have been recorded in the error object or not. We need a way to say “yes, there was an error, but do not attempt to record information about it again.”
0x80020102 SCRIPT_E_PROPAGATE Another internal code that we use to track the case where a recorded error is being propagated up the call stack to a waiting catch handler.
0x80020101 SCRIPT_E_REPORTED The script engines return this to the host when there has been an unhandled error that the host has already been informed about via OnScriptError.

This is only a subset of the errors that can be detected. Also many COM objects are designed to provide a scripting-friendly interface and therefore include call-back methods to allow the script to interrogate any errors raised in the COM object. For example in WMI:

   On Error Resume Next
   Set Test = GetObject _
              ("Winmgmts:root\cimv2:Win32_Printer.Name='TestPrinter'")
   Set WMI_Error = CreateObject("WbemScripting.SwbemLastEror")
   Wscript.Echo WMI_Error.Operation & VbTab & _
   WMI_Error.ParameterInfo & VbTab & WMI_Error.ProviderName

However, for the reasons I discuss in the next section, you should only need to exploit this richness in very limited circumstances.

Leave a Reply