After a Windows API call, how can I get the last error message in a textual form?
GetLastError() returns an integer value, not a text message.
After a Windows API call, how can I get the last error message in a textual form?
GetLastError() returns an integer value, not a text message.
Since c++11, you can use the standard library instead of FormatMessage:
#include <system_error>
if (!SomeWin32Function()){
DWORD error = ::GetLastError();
std::string message = std::system_category().message(error);
...
}
Here is my minimal C++ example using std::string/wstring.
#include <windows.h>
#include <string>
typedef std::basic_string<TCHAR> String;
String errorMessage(DWORD dwError)
{
LPTSTR lpBuffer = NULL;
String ret = TEXT("");
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, 0, (LPTSTR)&lpBuffer, 0, NULL))
ret = String(lpBuffer);
LocalFree(lpBuffer);
return ret;
}
It doesn't have any error checking though and just returns an empty string if it can't find the specified error. You can implement your own error checking if you like.
Why waste time write lot code, when few code do trick?
I pass 0 for dwLanguageId as it's the right way to do it, as other answers failed to notice that MAKELANGID macro is deprecated and should not be used as it is inconsistent and doesn't work at all for some languages.
Here is an excerpt from winnt.h in Windows SDK 10.0.19041.0 (2020-05-12) stating the issue:
//
// ** DEPRECATED ** DEPRECATED ** DEPRECATED ** DEPRECATED ** DEPRECATED **
//
// DEPRECATED: The LCID/LANGID/SORTID concept is deprecated, please use
// Locale Names instead, eg: "en-US" instead of an LCID like 0x0409.
// See the documentation for GetLocaleInfoEx.
//
// A language ID is a 16 bit value which is the combination of a
// primary language ID and a secondary language ID. The bits are
// allocated as follows:
//
// +-----------------------+-------------------------+
// | Sublanguage ID | Primary Language ID |
// +-----------------------+-------------------------+
// 15 10 9 0 bit
//
// WARNING: This pattern is broken and not followed for all languages.
// Serbian, Bosnian & Croatian are a few examples.
//
// WARNING: There are > 6000 human languages. The PRIMARYLANGID construct
// cannot support all languages your application may encounter.
// Please use Language Names, such as "en".
//
// WARNING: There are > 200 country-regions. The SUBLANGID construct cannot
// represent all valid dialects of languages such as English.
// Please use Locale Names, such as "en-US".
//
// WARNING: Some languages may have more than one PRIMARYLANGID. Please
// use Locale Names, such as "en-FJ".
//
// WARNING: Some languages do not have assigned LANGIDs. Please use
// Locale Names, such as "tlh-Piqd".
//
// It is recommended that applications test for locale names rather than
// attempting to construct/deconstruct LANGID/PRIMARYLANGID/SUBLANGID
//
// Language ID creation/extraction macros:
//
// MAKELANGID - construct language id from a primary language id and
// a sublanguage id.
// PRIMARYLANGID - extract primary language id from a language id.
// SUBLANGID - extract sublanguage id from a language id.
//
// Note that the LANG, SUBLANG construction is not always consistent.
// The named locale APIs (eg GetLocaleInfoEx) are recommended.
//
// DEPRECATED: Language IDs do not exist for all locales
//
// ** DEPRECATED ** DEPRECATED ** DEPRECATED ** DEPRECATED ** DEPRECATED **
//
Seems just that the information hasn't made its' way to the official MSDN doc of MAKELANGID yet.
Even if it did work correctly, it's the worse option since it tries to find the error string on that specified LangID and only that one ID, failing if it doesn't exist. Using 0 instead will very likely return at least something, even if that error isn't localized to the user's language.
Quote from MSDN FormatMessageW:
[in] dwLanguageId
The language identifier for the requested message. This parameter is ignored if dwFlags includes FORMAT_MESSAGE_FROM_STRING.
If you pass a specific LANGID in this parameter, FormatMessage will return a message for that LANGID only. If the function cannot find a message for that LANGID, it sets Last-Error to ERROR_RESOURCE_LANG_NOT_FOUND. If you pass in zero, FormatMessage looks for a message for LANGIDs in the following order:
- Language neutral
- Thread LANGID, based on the thread's locale value
- User default LANGID, based on the user's default locale value
- System default LANGID, based on the system default locale value
- US English
If FormatMessage does not locate a message for any of the preceding LANGIDs, it returns any language message string that is present. If that fails, it returns ERROR_RESOURCE_LANG_NOT_FOUND.