Fabulous Adventures In Coding
Eric Lippert is a principal developer on the C# compiler team. Learn more about Eric.
Apparently I've sparked a discussion amongst the super-geniuses of LtU on various innovative language constructs for handling errors. Fascinating stuff that I'd love to learn more about! But I'll be less highfalutin: no doubt about it, error handling in VBScript is a pain in the rear no matter how you slice it.
This series was inspired in party by an email from a reader who was interested in the philosophy of error handling. Here's an excerpt which contrasts two approaches: (my emphasis)
I think I'm old school in saying that error handling should be very tight. Handle errors where you expect to find them. Everything else is left to fail. I'd rather have a program end in a messy death than to blithely continue on in an unpredictable fashion. Some of my cohorts would rather do broad error handling (whole subroutines or sections of the script). They seem to assume that only the errors they expect will happen. And even if other errors do happen, it's better to have the script finish as best it can than to do nothing at all.
I've talked about this before (at the bottom of the post.) As a professional developer who writes complex C# and C++ code on a large team building a product that will be shipped to millions of people, I agree with the writer. Error handling should be built into the architecture from day one. And of course we have done so in VSTO2 -- we have developed a set of exception classes with error numbers and localizable error strings, and tried to engineer everything so that only the right errors get propagated up to the user.
But I'm working in C# and C++, languages specifically designed for implementing complex software written by large teams. VBScript is not such a language -- it was designed for simple administration and web scripts, where often "muddle on through" is exactly what you want it to do.
For example, I have a simple script much like this one that I use for doing quick-and-dirty regular expression searches on my hard disk. You’d better believe that On Error Resume Next is on for that script! If it encounters a directory or file that it cannot search because it is locked by another process, or that I don't have permission to read, or whatever, then I don't want my little twenty-line script to die horribly! I certainly don't want to be constantly maintaining it to add new error logic as I discover more special cases. Were I writing a user-grade bulletproof hard disk searching tool that was going to be shipped in the operating system, you'd better believe I'd have error handling all over that thing, but for my scripty purposes, doing its best and muddling through is almost always plenty good enough. Use the right tool for the job.
Finally, one more implementation detail that I forgot to mention the other day. When VBScript gets certain error numbers back from calls to IDispatch objects, it sometimes takes the error number and replaces it 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. On the off chance that someone finds this useful, I'll put the mapping table that VBScript uses below.
Coming up next time, part two of Riddle Me This, Google, where once more I dole out advice on subjects I know little about -- love, optics and primatology -- based on questions culled from my most recent 29950 Google hits. Stay tuned!
0x80004001 (E_NOTIMPL) --> 445 (ActionNotSupported)0x80004002 (E_NOINTERFACE) --> 430 (OLENotSupported)0x80020001 (DISP_E_UNKNOWNINTERFACE) --> 438 (OLENoPropOrMethod)0x80020003 (DISP_E_MEMBERNOTFOUND) --> 438 (OLENoPropOrMethod)0x80020004 (DISP_E_PARAMNOTFOUND) --> 448 (NamedParamNotFound)0x80020005 (DISP_E_TYPEMISMATCH) --> 13 (TypeMismatch)0x80020006 (DISP_E_UNKNOWNNAME) --> 438 (OLENoPropOrMethod)0x80020007 (DISP_E_NONAMEDARGS) --> 446 (NamedArgsNotSupported)0x80020008 (DISP_E_BADVARTYPE) --> 458 (InvalidTypeLibVariable)0x8002000A (DISP_E_OVERFLOW) --> 6 (Overflow)0x8002000B (DISP_E_BADINDEX) --> 9 (OutOfBounds)0x8002000C (DISP_E_UNKNOWNLCID) --> 447 (LocaleSettingNotSupported)0x8002000D (DISP_E_ARRAYISLOCKED) --> 10 (ArrayLocked)0x8002000E (DISP_E_BADPARAMCOUNT) --> 450 (FuncArityMismatch)0x8002000F (DISP_E_PARAMNOTOPTIONAL) --> 449 (ParameterNotOptional)0x80020011 (DISP_E_NOTACOLLECTION) --> 451 (NotEnum)0x8002802F (TYPE_E_DLLFUNCTIONNOTFOUND) --> 453 (InvalidDllFunctionName)0x80028CA0 (TYPE_E_TYPEMISMATCH) --> 13 (TypeMismatch)0x80028CA1 (TYPE_E_OUTOFBOUNDS) --> 9 (OutOfBounds)0x80028CA2 (TYPE_E_IOERROR) --> 57 (IOError)0x80028CA3 (TYPE_E_CANTCREATETMPFILE) --> 322 (CantCreateTmpFile)0x80030002 (STG_E_FILENOTFOUND) --> 432 (OLEFileNotFound)0x80030003 (STG_E_PATHNOTFOUND) --> 76 (PathNotFound)0x80030004 (STG_E_TOOMANYOPENFILES) --> 67 (TooManyFiles)0x80030005 (STG_E_ACCESSDENIED) --> 70 (PermissionDenied)0x80030008 (STG_E_INSUFFICIENTMEMORY) --> 7 (OutOfMemory)0x80030012 (STG_E_NOMOREFILES) --> 67 (TooManyFiles)0x80030013 (STG_E_DISKISWRITEPROTECTED) --> 70 (PermissionDenied)0x8003001D (STG_E_WRITEFAULT) --> 57 (IOError)0x8003001E (STG_E_READFAULT) --> 57 (IOError)0x80030020 (STG_E_SHAREVIOLATION) --> 75 (PathFileAccess)0x80030021 (STG_E_LOCKVIOLATION) --> 70 (PermissionDenied)0x80030050 (STG_E_FILEALREADYEXISTS) --> 58 (FileAlreadyExists)0x80030070 (STG_E_MEDIUMFULL) --> 61 (DiskFull)0x800300FC (STG_E_INVALIDNAME) --> 432 (FileNotFound)0x80030100 (STG_E_INUSE) --> 70 (PermissionDenied)0x80030101 (STG_E_NOTCURRENT) --> 70 (PermissionDenied)0x80030103 (STG_E_CANTSAVE) --> 57 (IOError)0x80040154 (REGDB_E_CLASSNOTREG) --> 429 (CantCreateObject)0x800401E3 (MK_E_UNAVAILABLE) --> 429 (CantCreateObject)0x800401E6 (MK_E_INVALIDEXTENSION) --> 432 (OLEFileNotFound)0x800401EA (MK_E_CANTOPENFILE) --> 432 (OLEFileNotFound)0x800401F3 (CO_E_CLASSSTRING) --> 429 (CantCreateObject)0x800401F5 (CO_E_APPNOTFOUND) --> 429 (CantCreateObject)0x800401FE (CO_E_APPDIDNTREG) --> 429 (CantCreateObject)0x80070005 (E_ACCESSDENIED) --> 70 (PermissionDenied)0x8007000E (E_OUTOFMEMORY) --> 7 (OutOfMemory)0x80070057 (E_INVALIDARG) --> 5 (IllegalFuncCall)0x800706BA (RPC_S_SERVICE_UNAVAILABLE) --> 462 (ServerNotFound)0x80080005 (CO_E_SERVER_EXEC_FAILURE) --> 429 (CantCreateObject)
Sometimes I end up rewriting my admin scripts in JScript just for the try/catch block!
excellent article. thanks for sharing.