Why does HRESULT begin with H when it’s not a handle to anything?

I got a piece of email from a colleague who wondered why HRESULT begins with an H when it's not a handle to anything.

"The question came up at lunch today, and your name soon followed. We all just assume you have some kind of time traveling phone booth or Tardis, and like Bill and Ted of old, move through geek history to find answers to those long lost questions."

As I understand it, in the old days it really was a handle to an object that contained rich error information. For example, if the error was a cascade error, it had a link to the previous error. From the result handle, you could extract the full history of the error, from its origination, through all the functions that propagated or transformed it, until it finally reached you.

For example, if a function receives an object from the client, and that object is expected to support a particular interface, but when the function asks the client for that interface, it gets the error "interface not supported", the function would transform the error to "invalid parameter" because the client passed an invalid object. When the caller receives the error object, it can unpack the error information to determine, "Okay, I got an invalid parameter error because the DoSomething method received an interface not supported error on its second parameter. Hey, that's me!"

It's great having all of this information available, but it comes at a cost. Everybody who generates or propagates or transforms errors needs to create or update the error object to include their piece of the puzzle. And every function call that consumes an error and decides not to propagate or transform it must remember to free the error object.

In practice, programs don't care about the nature of the error to this level of detail. They just want to know that an error occurred, and get a general idea of what kind of error it was. There's not much point creating all this detailed error information, only to have the app throw it away anyway.

The COM team decided that the cost/benefit simply wasn't worth it, so the HRESULT turned into a simple number. But the name stuck.

Comments (22)
  1. Antonio Rodríguez says:

    So, I understand that “in the old days” refers to the time when COM was being developed, before the first public versions. Interesting story.

  2. Motti says:

    < We all just assume you have some kind of time traveling phone booth or Tardis, and like Bill and Ted of old, move through geek history to find answers to those long lost questions.

    tl;dr; they were right

  3. Ian says:

    While its true that programs don’t care about the fine details of an error, they often need to answer simple questions such as: “Can I carry on?” or “Should I retry?” With Win32 error codes there’s no reliable generic way to answer questions like this and you end up trying to write a comprehensive switch block to cover all possible error codes. (Then of course you have to set up appropriate conditions to be able to test them all.) What is really unfortunate is that it’s no better with exceptions. The all too common practice of catching any exception, logging it, and carrying on is frankly terrifying. But there are very few libraries that make it easy to determine which exceptions should be caught, which exceptions represent transient errors, etc, so if you are so good as to catch just the exceptions you think you can handle there’s always the chance you’re missing exceptions that you could have handled. I wish that, for example, network libraries would derive all transient exceptions from a common base class so you have only one type to catch for the retry scenario.

    1. ErikF says:

      MS-DOS did have this capability in the form of extended error codes, but it was abandoned. The problem as I see it is that some operations may theoretically be retryable at the low level, but in practice they are not at the application level, and vice versa (applications might have fallback options, so failure of a network resource is not automatic failure of an operation, but simply switching to an alternate [non-network] mode.)

    2. JAS says:

      >The all too common practice of catching any exception, logging it, and carrying on is frankly terrifying.

      Terrifying in college textbooks, not so terrifying in real world code as long as you are mindful. If you’re one of those guys who just logs “An exception happened.” then yes you deserve to be terrified. When you log it all, call stack and line numbers and everything (screw obfuscation I ship with PDBs), you fix so many of them that whatever’s left has to be dire and then your catch evolves to treat them all as dire again. Of course, this requires active maintenance and total honesty about the code.

      1. Ian says:

        Logging all exceptions is great. It is the “and carry on” bit that I find terrifying. Terrifying because you now have no idea about your in-memory data structures, and consequently whether the plausible-looking final output is correct or not.

        1. Medinoc says:

          I think this “carrying on” bit makes sense in the cases where there are a lot of objects to process in a batch, and each one can independently fail (say, for reasons such as “this database row mentions a file but said file does not exist”). It’s easy to decide that one object failing must not interrupt the batch when all other objects have a chance of success.
          However, I like to add a clause “if the first X objects *all* fail, abort” to such batches.

          1. Ian says:

            Nah, I’d be worried that the exception could be one I never anticipated and that means the application is now in an unknown state. Carrying on from there is madness. The application could do literally anything with the rest of the objects in the batch (to take your example), including writing garbage to a database, committing random financial transactions, etc.

        2. Dave says:

          I too have given up on the “fail fast” religion. That’s fine for your college project, but not a professional app people depend on in the real world. Error messages only tick off most customers – unless they are tech-savvy they can’t *do* anything with that information anyways.

          I have switched to “sure, ok, whatever – go ahead and fail fast, and silently log the failure (inc debug info) to telemetry, and then do *everything* possible to keep stumbling along as if nothing happened. And for goodness sakes don’t tell the user about it because it will only scare and confuse them!”.

      2. IInspectable says:

        No need to ship PDB’s. Just drop a mini dump (MiniDumpWriteDump) and load it up in a debugger, that conveniently decorates it with symbol information from your local symbol server. A mini dump can include so much more context, up to the full program state, and makes log files look like an archaic remnant straight out of the Stone Age in comparison.

        1. JAS says:

          I do mean PDBs. I want the customer to have everything short of the original source files because I know how desperate it can get in the trenches at 3AM. Competitors seem to die off because they don’t execute, not because they don’t know how the algorithms work. Everyone knows how to do all the same things.

          Calling them log files might be misleading. I make sure that my latest software can always open a console to pipe out the live trace (nothing dumps text faster than the original Win32 console window). You eventually watch the trace more than the running program and balance out the trace statements so it tells you as much as possible without being overwhelming. Then the debugger ends up becoming a tool of last resort. Or even better, your customers do your “debugging” for you because they can and they don’t want to wait. You watch the trace like you watch the green letters of The Matrix because it “tells you stuff”.

          You can use StackFrame reflection in an efficient manner to prefix every trace line with the calling method. That’s when you start to scale as a developer because you no longer have to type them manually, so you can put trace messages everywhere quickly and they are guaranteed correct. You will have to manually unmangle some compiler-generated identifier patterns but it’s worth it.

          You will thank yourself if you make your interop HRESULT into an enum that contains every known value. Then you just hover over the variable and spare the Google/Bing lookup. Also you can dump it to the trace log with ToString(). I think mine has about 7000 values in it by now but I never ever regretted it. Add a [DebuggerDisplay] on the enum that breaks down the severity bit, facility, and code.

          Do the same thing with Win32 error codes, then create a GetLastError() wrapper that returns the strong typed code. Add a [DebuggerDisplay] on the enum that calls FormatMessage().

          Hope some of this helps someone somewhere. Suffice it to say that I believe unmanaged code is the biggest destroyer of developer time. Right down the toilet.

          1. morlamweb says:

            As someone “in the trenches”, thank you for providing PDBs and detailed error/diagnostic info from your software. It’s rather refreshing to see such a support-oriented outlook in the design and development phase. Much of the time, you’re left with an obscure error code or a meaningless message, and if you’re lucky, it’s enumerated on the Web.

    3. Tanveer Badar says:

      And if software industry as a whole and the greatest minds in it who have given us templates, generics, lambda expressions, async methods, , argument dependent lookup, VES, a boatload of garbage collectors etc. haven’t been able to come up with an organic approach to error handling then, perhaps it is time to realize we have been chasing the wrong goose for the last 30 years or so.

      1. Tanveer Badar says:

        I mean exceptions and error codes, not the wonderful ideas I was busy iterating at the start.

  4. Paul says:

    And there is also the IErrorInfo interface, if you want a little more detail. The error message is nice. I wonder when that was added.

  5. Mike says:

    OK, it seems I see now how the concept of dotnet InnerException property was born.

  6. JAS says:

    So everything they put into .NET Exception was more or less everything they originally wanted to do with HRESULT? Win32 Historian could be a real job soon.

    1. smf says:

      The “they” were likely to be different people. Anders Hejlsberg left Delphi (where he was head architect) and joined Microsoft where he originally created J++ and then .Net.

      .net has nested exceptions because java has nested exceptions.

      1. JAS says:

        Well, whoever is in charge of Win32Exception now needs to remember to set the HResult property. They go so well together!

  7. Peter Doubleday says:

    It’s interesting that nobody has ever defined (to my knowledge) what a “handle” might be.

    Now, you could take a rather prescriptive view on this, and insist that only “resources” have handles. At which point you have to define what a “resource” is. So maybe you want a “handle” to be shared, in some as yet undefined way. In which case you have to define what “shared” means. I suppose you could further refine this by now utterly useless concept to define a handle as “a kernel thingie, which for the sake of argument we are going to call an object, although it really doesn’t have to be anything that anybody would think of as an object, and depending upon the version of the OS this kernel thingie might be in kernel space or it might be in user space … oh, but it has to refer to a resource. Whatever that is. And it might or might not be shared. Whatever that means.”

    Me, I like to think of it as an opaque value which fills the requisite number of bits and might not be a value, but might actually be a pointer. In an opaque sort of way. It’s difficult to care much, either way.

    1. Zenith says:

      As I recall from another Old New Thing article, a handle is an internal data structure that they basically keep secret/opaque so that nobody relies on its feature set.

  8. Miroslav Martinovič says:

    … and then exceptions came along which decided that it’s nice to have this error stacking functionality after all…
    it’s all just repeating circles

Comments are closed.

Skip to main content