# Making Sense of HRESULTS

### Making Sense of HRESULTS

Every now and then -- like, say, this morning -- someone sends me this mail:

I'm getting an error in my JScript program.  The error number is -2147024877.  No description.  Help!

Making sense of those error numbers requires some delving into the depths of how COM represents errors -- the HRESULT.

An HRESULT 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.

Unfortunately, JScript interprets the 32 bit error code as a signed integer and displays it in decimal.  No problem -- just convert that thing back to hex, right?

var x = -2147024877;

print(x.toString(16))

Whoops, not quite.  JScript doesn't know that you want this as an unsigned number, so it converts it to a signed hex number, -0x7ff8ffed.  We need to convert this thing to the value it would have been had JScript interpreted it as an unsigned number in the first place.  A handy fact to know is that the difference between an unsigned number interpreted as a signed number and the same number interpreted as an unsigned number is always 0x100000000 if the high bit is set, 0 otherwise.

var x = -2147024877;

print((x<0?x+0x100000000:x).toString(16))

There we go.  That prints out 80070013.  Or, even better, we could just write a program that takes the error apart:

function DumpHR(hr)

{

if (hr < 0 )

hr += 0x100000000;

if (hr & 0x80000000)

print("Error code");

else

print("Success code");

var facility = (hr & 0x7FFF0000) >> 16;

print("Facility " + facility);

var scode = hr & 0x0000FFFF;

print("SCode " + scode);

}

DumpHR(-2147024877);

The facility codes are as follows

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

So you can see that our example is a Windows operating system error (facility 7), and looking up error 19 we see that this is ERROR_WRITE_PROTECT -- someone is trying to write to a write-protected floppy probably.

All the errors generated by the script engines -- syntax errors, for example -- are FACILITY_CONTROL, and the error numbers vary between script engines.  VB also uses FACILITY_CONTROL, but fortunately VBScript assigns the same meanings to the errors as VB does.  But in general, if you get a FACILITY_CONTROL error you need to know what control generated the error -- VBScript, JScript, a third party control, what?  Because each control can define their own errors, and there may be collisions.

Finally, here are some commonly encountered HRESULTs:

E_UNEXPECTED  0x8000FFFF "Catestrophic failure" -- something completely unexpected has happened.

E_NOTIMPL     0x80004001 "Not implemented" -- the developer never got around to writing the method you just called!

E_OUTOFMEMORY 0x8007000E -- pretty obvious what happened here

E_INVALIDARG  0x80070057 -- you passed a bad argument to a method

E_NOINTERFACE 0x80004002 -- COM is asking an object for an interface.  This can happen if you try to script an object that doesn't support IDispatch.

E_ABORT       0x80004004 -- whatever you were doing was terminated

E_FAIL        0x80004005 -- something failed and we don't know what.

And finally, here are three that you should see only rarely from script, but script hosts may see them moving around in memory and wonder what is going on:

SCRIPT_E_RECORDED   0x86664004 -- 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."

SCRIPT_E_PROPAGATE  0x80020102 -- 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.

SCRIPT_E_REPORTED   0x80020101 -- 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.

That's a pretty bare-bones look at error codes, but it should at least get you started next time you have a confusing error number.

• To be honest, I just use Windows Calculator in scientific mode. Punch in the decimal number, press the Hex radio button, select Dword. I tend to deal more with VB, where you often get FACILITY_ITF errors. FACILITY_ITF, of course, means 'defined by interface' - the interface itself defines what the error is. If you're a C developer or have access to the Platform SDK, you can find a lot of predefined error codes in WinError.h.
• Alexei Kvasov once wrote HRPlus, and then disappeared from the face of the web and took the tool with him. Fortunately, I saved a copy, and put it up on http://www.winwonk.com/utils/HRPlus.zip. It's a real timesaver for HRESULT spelunking.
• There's also the Error Lookup tool that comes with VC 6 (not sure if VS.NET has it since I don't use VS.NET). It's simple to write your own though, in fact the help for the FormatMessage() API has code that converts an HRESULT to a text description (if it's a standard error like E_OUTOFMEMORY).
• You can also quickly convert numeric value to E_xxx with 'hr' format specifier in VS debugger.
• Yes! That's one of those great features that no one knows about. The first thing I do in a new project is put two things in the watch window, "@ERR,hr" and "@EAX,hr" (@ERR shows the value of GetLastError(), another great feature that no one knows about)
• Dude, I'm on the Visual Studio team and I didn't know about the ERR trick! That's quite excellent! And yes, monitoring EAX is vital, particularly if you're tracking down the cause of an error and do not have debug symbols.
• Something like this could be useful: #define WIN32_FROM_HRESULT(hr) (HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : -1)
• That works all very well until someone says err = WIN32_FROM_HRESULT(MyFunction(x++)); These sorts of things are better done by inline functions!
• Taking numbers to and from HRESULTS, and pretty printing errors. Can be used as follows: catch( e ) { } Number.fromDWORD = function( n ) { var retVal = n & 0x7FFFFFFF; if( n & 0x80000000 ) retVal += 0x80000000; return retVal; } Number.prototype.toDWORD = function() { return (this<0x80000000) ? this : ( this - 0x80000000 ) | 0x80000000; } Number.fromHRESULT = function( n ) { return n.toDWORD(); } Number.prototype.toHRESULT = function() { return Number.fromDWORD( this ); } String.prototype.zeroFill = function( width ) { var s = ""; if( width > 0 ) s = String.zeros(width-this.length) + this; else s = this + String.zeros(width-this.length); return s; } var __ZEROS = "0000000000000000"; String.zeros = function( n ) { var s = ""; while( n>0 ) { s += __ZEROS.substr(0,Math.min(n,__ZEROS.length)); n -= __ZEROS.length; } return s; } Error.prototype.toString = function( optText ) { var s = ""; // If you have a function that retuns the current script name, then add it to "s" here /* TODO: I think accessing this.name causes havoc with JScript 5.6. So, for now, just fix it by forcing to "ERROR". if( !this.name ) s = "ComError"; else s += this.name.toString(); */ s += "ERROR"; if( this.number ) s += " (0x" + this.number.toHRESULT().toString(16).zeroFill(8) + ")"; s += ": "; if( optText ) s += optText + ": "; s += this.description; return s; }
• Nuts... hit add before I could finish or proof the above... As I was saying, can be used as follows: function println(v) { WScript.echo(v) } try { } catch( e ) { if( e.number.toHRESULT() != 0x80070013 ) { println( e.toString() ); } }
• What means error code 0x800a180e from Word::_Document interface?
Page 1 of 2 (19 items) 12