HRESULTs and Win32 Error Codes Introduction

Here’s a quick introduction to HRESULTS and Win32 Error Codes. There are essentially three steps. Get the error, Convert the error, and Look up the error.

Get the error:

  • Read it off the error message (someone else’s code)
  • Read the error from the Event Log (someone else’s code)
  • Call GetLastError() in your code.
  • Add @ERR pseudoregister to your Watch/QuickWatch Window. It will show the value you’d get if you called GetLastError() in your code from this thread.

Convert the error to the right format:

  • Many errors are displayed as decimals, and Win32 Error codes will be reported as a negative number. Drop the negative sign if present, then  convert the number to a DWORD.
  • It can be a bit tricky when the error is displayed as a DWORD. If the code doesn’t return anything helpful look for an 0x80XXYYYY value), try converting the DWORD to Decimal, making the Decimal negative, then converting the negative value back to DWORD
  • Some examples:
    • If our error code is 0x80070005 we’re already in a good format, and can simply look up the error.
    • If our error code is -2147024891, drop the negative and convert to Hexadecimal 0x80070005
    • If our error code  0x7FF8FFFB is reported, convert it to Decimal 2147024891, make that negative (-2147024891), then convert to DWORD 0x80070005

Look up the error:

Once you’ve got the error code, there are a number of methods available to convert that cryptic DWORD to a meaningful string.

  • Use Microsoft Error Lookup. Visual C++ Error Lookup (ERRLOOK.exe) is part of the Visual Studio .NET Tools, and has been included with Visual C++ installations “forever”. If the error code is not a system message, ERRLOOK provides the option of specifying a specific EXE or DLL module to translate the code to a message.
  • From within Visual Studio, type <errorcode>,hr in the Watch or Quickwatch window. This will format the error message as an HRESULT, and display the “friendly” error (e.g. 0x80070002,hr). This same formatting works with the @ERR pseudoregister, so @ERR,hr will format the error code as an error message, killing two birds with one stone by both capturing and translating the result code.
  • To routinely handle errors at runtime in your C++ code, see the MSDN topic.
  • If you’re doing COM Interop with Managed code then you better not need this introduction, but just in case, check out Adam Nathan’s comments and a FormatMessage shortcut tip from shawnfa on the .NET Security Blog.
  • I’m no VB expert, but I understand that if you’re using VB (classic), the LastDLLError property of the Err object is more reliable due to the nature of most VB applications. Francesco Balena implies that LastDLLError may have a significant performance impact.
  • Finally, if you really want to do it the hard way, you can translate it manually. In general, you’re going to get a number like this 0x80XXYYYY. Ignore the 0x80 part for now. What you care about is XXYYYY.
  • XX indicates the system this came from. See WINERROR.H for a complete list, but here are a few common system codes:
    • 8 Windows
    • 7 Win32 (non-Windows Microsoft stuff)
    • 25 HTTP
  • Look up the specific error value (YYYY) in MSDN’s list of system error codesWINERROR.H or your local copy of WINERROR.H. Here are a few of the more common error codes:
    •   0 ERROR_SUCCESS (aka NO_ERROR)
    • 2 ERROR_FILE_NOT_FOUND (err..the file is not present with that name)
    • 5 ERROR_ACCESS_DENIED (insufficient rights)
    • 6 ERROR_INVALID_HANDLE (are you sure that previous call succeeded?)

That’s a wrap, with two final caveats:

  • GetLastError can return unexpected values if system calls are being executed behind the scenes. If the error looks wrong… it might be.
  • Win32 Error Codes aren’t really HRESULTS, so be careful about using macros to check for success or failure. Know your return types and handle them appropriately.

What do you think?