How do I convert an HRESULT to a Win32 error code?

Date:November 3, 2006 / year-entry #373
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20061103-07/?p=29133
Comments:    32
Summary:Everybody knows that you can use the HRESULT_FROM_WIN32 macro to convert a Win32 error code to an HRESULT, but how do you do the reverse? Let's look at the definition of HRESULT_FROM_WIN32: #define HRESULT_FROM_WIN32(x) \ ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) \ : ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000))) If the value...

Everybody knows that you can use the HRESULT_FROM_WIN32 macro to convert a Win32 error code to an HRESULT, but how do you do the reverse?

Let's look at the definition of HRESULT_FROM_WIN32:

#define HRESULT_FROM_WIN32(x) \
  ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) \
: ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)))

If the value is less than or equal to zero, then the macro returns the value unchanged. Otherwise, it takes the lower sixteen bits and combines them with FACILITY_WIN32 and SEVERITY_ERROR.

How do you reverse this process? How do you write the function WIN32_FROM_HRESULT?

It's impossible to write that function since the mapping provided by the HRESULT_FROM_WIN32 function is not one-to-one. I leave as an execise to draw the set-to-set mapping diagram from DWORD to HRESULT. (Original diagram removed since people hate VML so much, and I can't use SVG since it requies XHTML.) If you do it correctly, you'll have a single line which maps 0 to S_OK, and a series of blocks that map blocks of 65536 error codes into the same HRESULT space.

Notice that the values in the range 1 through 0x7FFFFFFFF are impossible results from the HRESULT_FROM_WIN32 macro. Furthermore, values in the range 0x80070000 through 0x8007FFFF could have come from quite a few original Win32 codes; you can't pick just one.

But let's try to write the reverse function anyway:

BOOL WIN32_FROM_HRESULT(HRESULT hr, OUT DWORD *pdwWin32)
{
 if ((hr & 0xFFFF0000) == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32)) {
  // Could have come from many values, but we choose this one
  *pdwWin32 = HRESULT_CODE(hr);
  return TRUE;
 }
 if (hr == S_OK) {
  *pdwWin32 = HRESULT_CODE(hr);
  return TRUE;
 }
 // otherwise, we got an impossible value
 return FALSE;
}

Of course, we could have been petulant and just written

BOOL WIN32_FROM_HRESULT_alternate(HRESULT hr, OUT DWORD *pdwWin32)
{
 if (hr < 0) {
  *pdwWin32 = (DWORD)hr;
  return TRUE;
 }
 // otherwise, we got an impossible value
 return FALSE;
}

because the HRESULT_FROM_WIN32 macro is idempotent: HRESULT_FROM_WIN32(HRESULT_FROM_WIN32(x)) == HRESULT_FROM_WIN32(x). Therefore you would be technically correct if you declared that the "inverse" function was trivial. But in practice, people want to try to get "x" back out, so that's what we give you.

Now that you understand how the HRESULT_FROM_WIN32 macro works, you can answer this question, based on an actual customer question:

Sometimes, when I import data from a scanner, I get the error "The directory cannot be removed." What does this mean?

You will have to use some psychic powers, but I think you're up to it.

One unfortunate aspect of both HRESULTs and Win32 error codes is that there is no single header file that contains all the errors. This is understandable from a logistical point of view: Multiple teams need to make up new error codes for their components, but the winerror.h file is maintained by the kernel team. If winerror.h were selected to be the master repository for all error codes, it means that any team that wanted to add a new error code or change an existing one would have to pester the kernel team to make the change for them. Things get even more complicated if those teams have their own SDK. For example, suppose both the DirectX and Windows Media teams wanted to include the new winerror.h in their corresponding SDKs. If you install the SDKs in the wrong order (and how are you supposed to know which should be installed first, DirectX 8 or WMSDK 6?), you can end up regressing your winerror.h file. It's the version conflict problem, but without the benefit of version resources.

Many teams have prevailed upon the kernel team to reserve a chunk of error codes just for them.

Networking 2100–2999
Cluster 5000–5999
Traffic Control 7500–7999
Active Directory 8000–8999
DNS 9000–9999
Winsock 10000–11999
IPSec 13000–13999
Side By Side 14000–14999

There is room for only 65535 Win32 error codes, and over an eighth of them have already been carved out by these "block assignments". I wonder if we will eventually run out of error codes prematurely due to having given away error codes in too-large chunks. (Some sort of analogy with IPv4 could be made here but I'm not going to try.)


Comments (32)
  1. David Walker says:

    Oh, surely you could make an analogy with IPV4, if you really try!  :-)

  2. kokorozashi says:

    Reserving ranges of error codes has a cultural solution. As long as everyone on the inside of the firewall buys in, they can go to a central authority to request blocks as needed. 20 seems to be a good default block size. The central authority keeps track of which technologies have which blocks but doesn’t care about the individual values within each block. The values can be declared in any header that’s convenient at the time and moved into a central header once they’ve stabilized (shipped). The trick is in establishing the culture.

  3. John Burnett says:

    Did VML kick that commenter’s dog and run off with his wife?  Please don’t let a few bad apples spoil the use of a good teaching aid…  Besides, if they’re reading this blog, they should have "it’s for backwards compatibility!" pounded into their head by now.

  4. Speaking of error codes, the Exchange error lookup utility http://www.microsoft.com/downloads/details.aspx?familyid=be596899-7bb8-4208-b7fc-09e02a13696c&displaylang=en  is worth its weight in gold…

    It is not at all Exchange specific, just created by the Exchange team.

  5. Mark Sowul says:

    I agree, please don’t remove your diagrams because a few people are whining about them.  Something is better than nothing.

  6. Why not just use an <img> for diagrams?

    [I already answered this. Besides, bitmaps don’t resize well. -Raymond]
  7. Cody says:

    "Why not just use an <img> for diagrams?"

    Because he can’t upload to the blog.  If he really wanted he could host them on Photobucket or some such, but meh.

  8. more and more errors in future versions of Windows ;p says:

    I wonder if we will eventually run out of error codes prematurely due to having given away error codes in too-large chunks.

    Well when that day comes, they’ll just add an GetLastErrorEx() function to get an extended error code.  GetLastError() would then be deprecated and will return a fixed value for all the errors that don’t fit in the old range of error values.

  9. andy says:

    No matter how many problems there are with exceptions this is one area where it beats error codes totally:

    Win32Exception

    |

    +– NetworkException

    +– ClusterException

    +– TrafficControlException

    +– ADException

    +– DNSException

    +– WinsockException

    +– IPSecException

    +– SoSException

    (Guess you were expecting a comment like this ;)

    Btw: I’ll happily startup Internet Explorer (even download the newest version) if it is required to read this blog. There are Firefox-extensions available to quickly open a page in Internet Explorer… so please do not worry so much about the anti-VML crowd!

  10. BryanK says:

    > I get the error "The directory cannot be removed." What does this mean?

    It very likely means something is using WIA instead of TWAIN, and trying to map a WIA error HRESULT to a Win32 error code to get an error description string out of it.  (Because AFAIK there’s no way to get *any* description strings out of a general HRESULT, unless the class that generated the error supports IErrorInfo.)

    The reverse mapping is colliding, and they chose whatever Win32 error code maps to "the directory cannot be removed".

    What they should probably do is provide the full HRESULT value instead, which can then be looked up in the WIA docs (hopefully) or the information provided by the WIA provider (hah, yeah right!).  Of course this is probably "confusing" to the user — but I’d say it’s no more confusing than "the directory cannot be removed"!

  11. Goran says:

    -1 for andy

    Yes, exceptions are mostly superior for error handling, but what
    about code in languages without them (C)? Also, changing behaviour of
    existing APIs would break too much existing code. (That said, I like
    that giving bad HTREEITEM to TreeCtrl takes me down!)

    +1 for kokorozashi

    In fact, the whole process could be 100% automated. Warning: little
    Peter’s business process follows! One sends a mail (to:
    “WinError.h@kerneldevs.ms.com” subject: “New Error Code” body:
    “MY_ERROR_CODE”) to a server; server modifies WinError.h and responds
    to you (“ready, MY_ERROR_CODE is 1234567”); one gets WinError.h from
    source control!

    [That doesn’t solve the SDK distribution problem,
    though, as I noted in the article. There’s also more to adding an error
    code to the system than just getting a number assigned. -Raymond
    ]
  12. Ashleigh says:

    Take a look at the way Vax / VMS managed error codes. It was way cool. A 32 bit number encoded all manner of goodies, including the sub-system, severity, error, etc.

    AND it was extensible by a user… they had some magic method of loading new error definitions from a file into a running system, and it applied on a per-user basis.

    Way superior to anything I have ever seen, before or since.

  13. James says:

    Yes, Andy, there’s an extension for Firefox (IETab) which opens
    pages in IE. Now I just need to find the URL for the Mac version of IE
    … oh wait. It seems to have disappeared for some reason…

    The update ‘problem’ isn’t a real problem, though: nothing even the
    most trivial versioning system wouldn’t solve. When I install Foo SDK,
    which installs winerror.h version 1437; installing Bar SDK the next
    day, which included version 1293, detects it’s an older version and
    doesn’t override.

    Having said that, there’s a better reason for using blocks: as
    Ashleigh mentions about VMS, it gives you an identity of the subsystem
    which returned an error, even without looking up the specifics of that
    particular error code.

    The aliasing problem rears its ugly head quite often (that, or lazy
    error handling!): a certain word processing application happens to give
    rather a lot of “out of memory” errors when printing goes wrong, even
    when there’s plenty of memory…

    [And as I noted in the posting, text files don’t
    have version resources. Besides, you create the new problem that a base
    header file (winerror.h) is being shipped “out of band”. Managing out
    of band releases is a major headache. -Raymond
    ]
  14. Paul Dougherty says:

    VxDs had an ID code range issue too. (ID codes are used by 16-bit user-level code to interface to VxDs). Originally, MS handed out ranges of IDs, but MS had to stop because they were going to run out.  Instead, you had to request each ID individually.

  15. James says:

    “And as I noted in the posting, text files don’t have version resources.”

    True, for now; on the other hand, a decade ago Windows didn’t have anti-aliasing either. Adding a version number to a text file isn’t exactly the halting problem.

    “Besides, you create the new problem that a base header file (winerror.h) is being shipped “out of band”. Managing out of band releases is a major headache.”

    Utterly trivial, given a dependency tracking system: Foo SDK v2 depends on Core SDK v1.3. Now you just need some sort of Update facility for Microsoft products…

    (Indeed, I seem to recall one specifically for the various SDKs available, although I haven’t seen it recently.)

    [But where do you put the version number? You can’t put it in the file name (obviously since that makes it a different file), and you can’t put it in the content since there is nowhere to put it without changing the semantics of the file. And FTP and email attachments destroy any other metadata. (I don’t see how a dependency system “solves” the out-of-band release problem. “I downloaded some source code from MSDN and it doesn’t build. I asked my friends and one of them told me I needed to run a dependency tracker (Who knew?). The dependency tracker says I need to download and install version 22.1.5.1 of the ODBC SDK. What the heck is that all about? I’m not using ODBC! Or, “I just downloaded the latest DirectX SDK and it tells me that it has a dependency on ODBC 22.1.5.1. What the heck is that all about?” -Raymond]
  16. Stefan Kuhr says:

    Hi Raymond,

    I don’t see why anyone should run out of numbers, because it is not only the lower word and the error bit that makes up error codes found in winerror.h. You still have the facility bits and for me the facility bits seem to be a natural choice for error codes of different teams. This is also what the error codes in winerror.h suggest, where there are at least 14 facility codes defined. Or are even the facility bits owned by the kernel team?

    (At least this is how we did it in the company I am working for: Our error codes all have bit 29 set and every team gets a facility assigned. Within that per-team-facility, the team is free to assign error codes on their own in the low word of the error code.)

    [You’re confusing HRESULTs and Win32 error codes. HRESULTs have facility bits. Win32 error codes don’t. -Raymond]
  17. Carlos says:

    A unified error numbers file causes other problems.  Every module in your system will have a dependency on it, so whenever you add a new error number (which may be for something peripheral) you need to rebuild the entire system.  This is usually impracticable.

    You can cheat by pretending the dependency doesn’t really exist (which is what vc++ used to do with resource files).  This works most of the time, but can result in some very nasty bugs if you actually did need to rebuild everything.

  18. Dean Harding says:

    I’d say the way you’d implement the "versioned header file" thing would be that the file itself wasn’t versioned, but that the package it was included in was – and in the case of a "global error code" file, it would be included in a package all of its own.

    So ODBC v3.45.2 might depend on "global header file" v12.34.5 and Windows Media SDK v9.6.3 might depend on "global header file" v12.43.2. Your package management program would ensure that you always have the required version of "global header file" when you go to download its dependent SDK.

    A sample on MSDN might say, "this sample requires version 9.32 of the Windows Media SDK to compile" which would, in turn, require a certain version of the "global header file" package.

    Of course, that’s a pretty big effort just so you can have one place to look for error codes, and I’d say there’s plenty of BETTER reasons for developing an SDK update site than this one…

  19. James says:

    “But where do you put the version number? You can’t put it in the file name (obviously since that makes it a different file), and you can’t put it in the content since there is nowhere to put it without changing the semantics of the file.”

    Er – no comments in your source code?

    /* winerror.h v 3.14 (05 Nov 2006) */

    Or define a preprocessor symbol, so any code using the header can check versions for itself.

    “I don’t see how a dependency system “solves” the out-of-band release problem. “I downloaded some source code from MSDN and it doesn’t build.”

    Or: “I downloaded some source from MSDN which said it needs winerror.h version 3.14, and the comment in my copy of winerror.h says it’s only version 3.11. Do you think that means I need an update?” Run the updater, end of problem.

    “The dependency tracker says I need to download and install version 22.1.5.1 of the ODBC SDK. What the heck is that all about?”

    Well, that hypothetical update site would help out there. Or a search for ‘odbc sdk’. Apart from which: does all your ODBC code compile just fine with a very old SDK, or none at all? If so, how? Of course, I was assuming said tracker would do the downloading, or explain it.

    [Sure, a comment might work for a C/C++ header file, but what about a readme file or an HTML file or an XML file? They’re text files too. As for the dependency tracker: I bet most of the time, people who run into a build problem don’t say “Let me run the header file updater; that’ll fix it.” Often, updating your header files introduces a new problem. -Raymond]
  20. dave says:

    Take a look at the way Vax / VMS managed

    error codes. It was way cool. A 32 bit

    number encoded all manner of goodies,

    including the sub-system, severity, error,

    etc.

    And that’s how the NT kernel, and some OS-provided usermode stuff, handles error codes. For fairly obvious reasons :-)

    Here’s a snippet from ntstatus.h, which is output by the message compiler (I hope it survives posting with format intact).

    //  Values are 32 bit values layed out as follows:

    //

    //   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1

    //   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0

    //  +—+-+-+———————–+——————————-+

    //  |Sev|C|R|     Facility          |               Code            |

    //  +—+-+-+———————–+——————————-+

    The trouble comes from Win32 error codes, which are just plain integers (I assume for back-compatibility with 16 bit code, which was in turn back-compatible with DOS).

    And then there’s the fact that HRESULTs are sort-of similar to NT status codes, but different (there’s some detail in the high bits that I’ve forgotten about).

  21. James says:

    I was referring to header files specifically, rather than text files in general, although comments should also work for versioning HTML or XML files in a similar way. I’d say that even among users who still have the mouse on the floor and wonder why the foot pedal doesn’t do anything could generally figure out that the error ‘this software requires a newer version of …’ means they need to update something.

    Dean: Whatever the reasons, presumably they seemed good enough at one point: http://www.microsoft.com/msdownload/platformsdk/sdkupdate/

    [The error message isn’t “This software requires a new version of…” The error message is “undeclared identifier”. -Raymond]
  22. Dean Harding says:

    James: I know there’s plenty of good reasons for having one. It’s just not THIS reason ;)

  23. Peter says:

    RCS, a standard source control system for Unix, has largely solved the ‘put a version number in a text file’ problem.  The ID string starts off like this:

    $Id$

    and is converted by RCS to look like this:

    $Id:zzzzzzz$

    where zzzzz is the id number.  In normal practice, the $Id$ is put into a comment:

    /* For C and C++: $Id$ */

    C    FOR FORTRAN IV $Id$

    REM  For Basic $Id$

    Some of the downsides: humans have to be clever enough to find a space that is properly compatible, the length of the ID string is variable (so no putting it into a Hollerith constant, Fortran fans!).

    And to give Raymond the biggest reason against the system that I can think of: if Microsoft picked this system blindly, they would be “polluting” RCS: they would be shipping files that can’t easily be put into an end-user’s RCS system.

    [But the file version in the file isn’t the same as the internal version in revision control. For example, if you look at the file version of kernel32.dll, it’s a 128-bit value (broken into four 32-bit fields), not a simply incrementing number. File version information is more than just a number, too. You need language information, publisher information, etc. And some text files don’t have the ability to “hide” text in the form of comments. (E.g., /usr/dict/words, .reg files) -Raymond]
  24. stegus says:

    Hmm, Raymond, I am not sure what you are trying to say here.

    As far as I understand, HRESULT_FROM_WIN32(x) should be used to get
    a HRESULT from a windows error code – or more specifically a value
    returned by GetLastError().

    Are you saying that GetLastError() sometimes return a value > 0xffff ?

    Or, are you saying that the values returned by GetLastError() can overlap between different parts of windows ?

    Or, are you saying that some developers mistakenly use HRESULT_FROM_WIN32(x) with non-windows error codes?

    Or, are you saying that some developers assume that the lower 16
    bits of an arbitrary HRESULT can always be passed to FormatMessage
    regardless of the Facility code ?

    The way I have interpreted the situation is that GetLastError()
    always returns a unique 16-bit value for each error, and you can pass
    that code to FormatMessage to get the error message text.

    If you use HRESULT_FROM_WIN32(x) as it is intended – by inserting a
    16-bit windows error code, it is very possible to perform the reverse
    operation. That is exactly what your WIN32_FROM_HRESULT function does.

    [My point is that not all HRESULTs come from Win32. See the puzzle. -Raymond]
  25. A. Skrobov says:

    Would this be the solution?

    * all error codes are collected into a single versionable binary file, like a TLB or a database;

    * each SDK installer first updates the database, then generates winerror.h from the updated database.

  26. stegus says:

    I’m sorry, but this time you are really not making yourself very clear.

    In your post, you where talking about implementing an inverse for the macro HRESULT_FROM_WIN32 – this is no problem at all if the macro is used as it is intended (with 16-bit values)

    Now it seems that your real point is that it is impossible to retrieve a win32 error code from an arbitrary HRESULT. Of course this is true. You can only ever hope to retreive a win32 error code if the facility of the HRESULT is FACILITY_WIN32.

    The facility defines how to interpret the error code. If the facility is FACILITY_ITF for example, the error code is private to the interface that returned the error, which means that the only way to get any information about the interpretation about the code is to consult the documentation for the interface you are calling.

    All of this has nothing to do with implementing an inverse for HRESULT_FROM_WIN32.

    A HRESULT with another facility than FACILITY_WIN32 was never created with that macro in the first place.

    [As with most of my entries, the topic was inspired by a question, in this case, the title question. It came from the customer whose question was highlighted at the end. The point is that the mapping between HRESULT and Win32 errors is not one-to-one. -Raymond]
  27. stegus says:

    It is true that it is not possible to extract a win32 error from any arbitrary HRESULT, but the reason for this is NOT that HRESULT_FROM_WIN32 maps multiple different error codes to the same HRESULT.

    The answer to the question in the title: "How do I convert an HRESULT to a Win32 error code?" is very simple:

    If the facility code is FACILITY_WIN32, the win32 error code should be in the lower 16 bits, if any other facility – it is impossible to get a win32 error code.

    You are confusing the issue by talking about how HRESULT_FROM_WIN32 maps multiple different error codes to the same HRESULT. This is not true if the macro is used as intended, and in any case it is simply not relevant for what you are trying to say.

  28. Here’s that mapping diagram.

  29. Neil says:

    Sorry if I’ve overlooked this one, but why does Win32 use error codes instead of HRESULTs?

  30. BryanK says:

    Because Win32 isn’t COM, and HRESULTs are a COM thing.

    ;-)

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index