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.