August 26th, 2021

In C#, how do I get the descriptive text for an HRESULT?

Suppose your C# code manages to find in its hands a COM HRESULT value. How do you convert that to a descriptive string?

One idea is to p/invoke to the Format­Message function, but that’s awfully cumbersome. Maybe we can live off the land.

using System;
using System.Runtime.InteropServices;

class Program
{
    public static string MessageFromHResult(int hr)
    {
        return Marshal.GetExceptionForHR(hr).Message;
    }

    public static void Main()
    {
        var E_BOUNDS = unchecked((int)0x8000000B);
        System.Console.WriteLine(MessageFromHResult(E_BOUNDS));
    }
}

The result:

The operation attempted to access data outside the valid range (Exception from HRESULT: 0x8000000B)

We ask the Marshal class to create an exception from the HRESULT and extract the Message from it. This does do more work than necessary, because it also does additional exception-related stuff, like capturing the COM error context into the exception object. This is wasted work, but on the other hand, the convenience is hard to beat.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

11 comments

Discussion is closed. Login to edit/delete existing comments.

  • Mystery Man · Edited

    Microsoft’s official suggestion seems to be the System.ComponentModel.Win32Exception class. Something like this:

    [System.ComponentModel.Win32Exception]::new(0x8000000B).Message

    Or… if you want it in C#:

    using System;
    using System.ComponentModel;
    
    class Program
    {
        public static void Main()
        {
            var E_BOUNDS = unchecked((int)0x8000000B);
            Console.WriteLine((new Win32Exception(E_BOUNDS).Message));
        }
    }
    • Ivan K

      Yeah I’m pretty sure I also used win32exception. Maybe it has the same overhead, and the other way also probably handles win32 errors like “5”. Anyway, I’m a bit sad because I think in 5 +/- years time all my windows code will be obsolete, to me at least :(.

  • switchdesktopwithfade@hotmail.com

    Create a 7,300+ line HRESULT enum with every single Microsoft HRESULT value that ever existed. Just do it. Then ToString( ) tells you exactly the rich Win32 string identifier at runtime, which you can put in your trace or use to more quickly find tech support. I’ve never looked back. The code is only tedious the first time, then you never have to write it again for the rest of your life.

    • Martin Ba

      Somehow it is a boon that most user facing code does NOT display the Win32 error code identifier. (like e.g. ERROR_CIMFS_IMAGE_CORRUPT)
      When googling for errors and you just include the FormatMessage Text and the Hex Code, you often find general help and troubleshooting.
      When including the error identifier / macro name the search results tend to be more developer focused, precisely because it is not what the common user would see.

      Of course, the fact that...

      Read more
    • Raymond ChenMicrosoft employee Author

      Do you update the enum each time a new Windows version is released, to add the new error codes? Or is your list just frozen from the point you wrote it originally? For example ERROR_CIMFS_IMAGE_CORRUPT was added in 19041.

      • switchdesktopwithfade@hotmail.com

        I always aggressively update the enum to every single new code I discover on the latest Windows version whether I use it or not, but the updated enum only makes it out with new app builds. IIRC there used to be a page long ago on the MS website that showed the Win32 API changes from version to version and that was nice. I should have a more elaborate system of diff-ing API header files...

        Read more
      • Raymond ChenMicrosoft employee Author

        If you have to update it at each release of Windows, I don’t see how that fits in with the “you never have to write it again for the rest of your life” claim.

      • Antonio Rodríguez

        Not only that. Even if he took the effort to update the error message table with each new version of the Windows SDK, you still would have the problem of an user running a version of the app from 2016 in Windows 10 21H1. Go tell the user that the "Error: error description not found" message is because s/he is still using the last version of the app s/he paid for. Messages of that kind...

        Read more
  • Henke37

    To be fair, if you care about a HRESULT, you usually want to throw it as a managed exception anyway.

    • Daniel Sturm

      And most of the time the interop layer takes care of converting the HResult to an exception anyhow.

      Still I had situations where I had to pinvoke FormatMessage (because I wasn’t aware of this helper method), but I can’t for the life of me remember why.

      Probably incompetence (or inexperience) on someone’s part (most likely mine).

    • Antonio Rodríguez · Edited

      Right. But sometimes you may need to log it or show an human-readable message to the user. That's where this trick comes handy. It's not very useful to get errors like "Error 0x80012345", and certainly not user friendly at all. Personally, I do hate it when I get them and I'm forced to search the code on the Internet. I doubly hate it when the search results come from a more popular package which uses...

      Read more