Errors in Windows - DWORD (GetLastError) vs HRESULT vs LSTATUS
Asked Answered
S

1

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.

Shumate answered 23/10, 2013 at 16:37 Comment(0)
R
35

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.

Roughshod answered 23/10, 2013 at 17:37 Comment(6)
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?Shumate
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.Roughshod
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.Shumate
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).Gar
I've checked it. FormatMessage returns the same string for both DWORD and HRESULT error values.Shumate
Well, it seems that converting WINAPI codes to HRESULTs is possible, but not the other way round. I then don't understand an example from Microsoft: 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?Coercion

© 2022 - 2024 — McMap. All rights reserved.