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

FACILITY_METADIRECTORY 35

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.

Tags COM Programming JScript Scripting VBScript

Comments (19)

You must be logged in to post a comment.
Mike Dimmick
October 22, 2003 at 2:10 pm
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.
Log in to Reply
Kim Gräsman
October 22, 2003 at 4:43 pm
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.
Log in to Reply
Mike Dunn
October 22, 2003 at 4:58 pm
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).
Log in to Reply
KiwiBlue
October 23, 2003 at 9:15 am
You can also quickly convert numeric value to E_xxx with ‘hr’ format specifier in VS debugger.
Log in to Reply
Mike Dunn
October 23, 2003 at 10:39 am
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)
Log in to Reply
Eric Lippert
October 23, 2003 at 1:07 pm
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.
Log in to Reply
Village Idiot
October 23, 2003 at 11:22 pm
Something like this could be useful:
#define WIN32_FROM_HRESULT(hr) (HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : -1)
Log in to Reply
Eric Lippert
October 24, 2003 at 1:59 pm
That works all very well until someone says
err = WIN32_FROM_HRESULT(MyFunction(x++));
These sorts of things are better done by inline functions!
Log in to Reply
Jay Hugard
October 24, 2003 at 6:41 pm
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;
}
Log in to Reply
Jay Hugard
October 24, 2003 at 6:44 pm
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() );
}
}
Log in to Reply
Fabulous Adventures In Coding
August 3, 2004 at 10:31 am
Log in to Reply
Sohail Kadiwala
November 18, 2004 at 2:47 am
Log in to Reply
Fabulous Adventures In Coding
December 5, 2004 at 10:24 pm
Log in to Reply
Pelargir – Musings on software and life from Matthew Bass. » Decoding COM HRESULT error codes
September 2, 2006 at 9:25 am
PingBack from http://www.matthewbass.com/blog/2005/11/15/decoding-com-hresult-error-codes/
Log in to Reply
Alexey
September 28, 2006 at 9:55 am
What means error code 0x800a180e from Word::_Document interface?
Log in to Reply
Fabulous Adventures In Coding
May 8, 2007 at 12:30 pm
Reader Shaka comments on my post about error messages that “catastrophic failure” really does take the
Log in to Reply
Jerome
September 23, 2007 at 8:23 am
Excellent, thx.
Thx to Jay Hugard too for his code
Log in to Reply
Jay Hugard
June 29, 2010 at 6:25 pm
I have a scriptable COM component (implements IDispatchEx) which has a method that takes a script-function as a parameter (an IDispatch* as a parameter). My COM component calls back into script via that supplied IDispatch parameter.
If the script-function happens to throw an exception, then my call to Invoke will return SCRIPT_E_PROPAGATE. If I return that HRESULT back to the caller of my COM component, then the script engine throws an error.
However, the *ACTUAL ERROR* is nowhere to be found. It’s not returned in the Invoke HRESULT (that’s SCRIPT_E_PROPAGATE). It’s not returned via the EXCEPINFO supplied to Invoke (the structure remains empty). AND, it’s not available via GetErrorInfo (the call returns S_FALSE)!
I’d *really* like to get my hands on that error, because it would be useful to cache it and return the same error on subsequent calls (getting to the error often involves an expensive operation that is defined by the script-function passed as a parameter, but I do know how to cache the error). Is there a way for a scripted COM component to get to an exception thrown during a call-back into a supplied script-function???
Hmm. I have not looked at that code for ten years (and I didn’t understand it real well then; I didn’t write the exception handling logic), so I’m a bit rusty. The EXCEPINFO ought to be filled in with the details. Do you know what exactly is being thrown by the script? Do you know if there are any possible handlers up the stack? The engine should interrogate the IDispatchEx to see if it can determine a call stack that has a catch block. Is there any such call stack?
Log in to Reply
Jay Hugard
July 2, 2010 at 10:20 am
Thanks for responding, Eric!
Worked this out after tracing into the assembly for the JScript engine. See the following Question & Answer on StackOverflow.
stackoverflow.com/…/3167705
Man, was that a tough mystery to unravel Seriously underdocumented!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s