26

I'm doing some programming in Win32 + WTL, and I'm confused with the available types of errors.

In general, I want to check for an error, and feed it to AtlGetErrorDescription (which calls FormatMessage).

My questions are:

  1. What's the difference between:

    • DWORD, returned by GetLastError.
    • HRESULT, returned by e.g. the CAtlFile wrapper, which uses HRESULT_FROM_WIN32 to convert from DWORD.
    • LSTATUS, returned by e.g. RegCreateKeyEx.
  2. Which types of errors can I feed to FormatMessage? Its signature indicates it accepts HRESULT, but there are lots of examples where the return value of GetLastError is directly passed to FormatMessage.

Paul
  • 6,061
  • 6
  • 39
  • 70

1 Answers1

34

They just reflect different APIs used in Windows:

  • GetLastError() returns a winapi error code. A simple number starting at 1. They are usually mapped from an underlying native api error code. Like ERROR_FILE_NOT_FOUND is mapped from the STATUS_OBJECT_NAME_NOT_FOUND file system driver error code. Winapi error codes are declared in the WinError.h SDK header file. You can count on getting a descriptive string from FormatMessage() with the FORMAT_MESSAGE_FROM_SYSTEM option.

  • An HRESULT is a COM error code. It is built up from three basic parts, the high bits indicate the severity, the middle bits encode the facility which indicates the source of the error, the low 16 bits encode an error number. The HRESULT_FROM_WIN32() macro is a helper macro to map a winapi error code to a COM error code. It just sets the severity to "fail", the facility code to 7 (winapi) and copies the error code into the low bits. There are a lot of possible COM error codes and only a few of them are convertible to a string by FormatMessage(). You should use the ISupportErrorInfo interface to ask if the COM server can provide a description of the error through IErrorInfo.

  • LSTATUS is obscure, RegCreateEx actually returns LONG, just the winapi error code. It does pop up in some shell wrapper functions, like SHGetValue(). It is often very unclear to me why the shell team does what it does.

  • Not mentioned in your question but worth noting are the error codes generated by the native api. They are documented in the ntstatus.h SDK header. The winapi is supposed to wrap the native api but these error codes do peek around the edges sometimes, particularly in exceptions. Most any programmer has seen the 0xc0000005 (STATUS_ACCESS_VIOLATION) exception code. 0xc00000fd matches this site's name. FormatMessage() can convert the common ones to a string as long as it wasn't a custom error code generated by a driver. There are several apis that use these kind of error codes, even though they run in user mode. Common examples are WIC and Media Foundation, otherwise without a strong hint why they preferred it this way. Getting a string for such an error code requires using FormatMessage with the FORMAT_MESSAGE_FROM_HMODULE option.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 2
    Thank you for the detailed answer. Does it mean that `FormatMessage` can handle both `GetLastError()` and `HRESULT_FROM_WIN32(GetLastError())`? Also, I don't understand why sometimes ATL wrappers return the WINAPI error code (e.g. `CRegKey`), and sometimes the `HRESULT` code (e.g. `CAtlFile`). Is it OK to keep either result in, say, a DWORD, and pass the error, if any, to `FormatMessage`? – Paul Oct 23 '13 at 17:49
  • ATL is heavily geared towards COM so seeing it return COM error codes is not unusual. But yeah, it isn't consistent about it. Just go by the return error type to see the difference. And yes, it is not like you have another oracle than FormatMessage() to give you a string. – Hans Passant Oct 23 '13 at 17:56
  • You still haven't answered whether `FormatMessage` accepts both type of error messages. I guess I'll just try it out :) Another inconvenience is that they have different ways of indicating success/failure - `==ERROR_SUCCESS` vs `SUCCEEDED`. So perhaps there's no other way than sticking to one of the two - and because AFAIK there's no way to convert `HRESULT` to the WINAPI error code, I guess I'd have to use `HRESULT`. That's pretty much sucks, as I'd have to use `HRESULT_FROM_WIN32` quite a lot. – Paul Oct 23 '13 at 18:11
  • 3
    `ERROR_SUCCESS` is 0, as is `S_OK`. `SUCCEEDED()` looks for any `HRESULT` value that is `>= 0`, which would include `S_FALSE` (1), which is not an error. `GetLastError()` returns a non-zero value for errors. `HRESULT` uses values `< 0` for errors. To get the win32 error code from an `HRESULT`, you can use `HRESULT_CODE()` if `HRESULT_FACILITY()` returns `FACILITY_WIN32` (7). – Remy Lebeau Oct 23 '13 at 19:00
  • I've checked it. `FormatMessage` returns the same string for both `DWORD` and `HRESULT` error values. – Paul Oct 23 '13 at 22:19
  • 1
    Well, it seems that converting `WINAPI` codes to `HRESULT`s is possible, but not the other way round. I then don't understand an example from Microsoft: http://us.codeforge.com/read/219594/MgSc.h__html . There, functions are defined which internally call both `WINAPI` and `COM` functions. However, the functions in the example return a `DWORD` error code, which I understand as `WINAPI`. In the implementation, many casts of type `(DWORD)SOME_HRESULT_CODE` can be seen. Does it make sense? Why isn't the API crafted the other way round? – Martin Pecka Jan 27 '16 at 23:28