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.
Microsoft’s official suggestion seems to be the System.ComponentModel.Win32Exception class. Something like this:
Or… if you want it in C#:
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 :(.
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.
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...
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.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...
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.
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...
To be fair, if you care about a HRESULT, you usually want to throw it as a managed exception anyway.
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).
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...