Without:
How can I use FormatMessage() to get the error text for a HRESULT
?
HRES
Here is a version of David's function that handles Unicode
void HandleLastError(const TCHAR *msg /* = "Error occured" */) {
DWORD errCode = GetLastError();
TCHAR *err;
if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPTSTR) &err,
0,
NULL))
return;
//TRACE("ERROR: %s: %s", msg, err);
TCHAR buffer[1024];
_sntprintf_s(buffer, sizeof(buffer), _T("ERROR: %s: %s\n"), msg, err);
OutputDebugString(buffer);
LocalFree(err);
}
Since c++11, you can use the standard library instead of FormatMessage
:
#include <system_error>
std::string message = std::system_category().message(hr)
As pointed out in other answers:
FormatMessage
takes a DWORD
result not a HRESULT
(typically GetLastError()
).LocalFree
is needed to release memory that was allocated by FormatMessage
I took the above points and added a few more for my answer:
FormatMessage
in a class to allocate and release memory as neededoperator LPTSTR() const { return ...; }
so that your class can be used as a stringclass CFormatMessage
{
public:
CFormatMessage(DWORD dwMessageId,
DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)) :
m_text(NULL)
{
Assign(dwMessageId, dwLanguageId);
}
~CFormatMessage()
{
Clear();
}
void Clear()
{
if (m_text)
{
LocalFree(m_text);
m_text = NULL;
}
}
void Assign(DWORD dwMessageId,
DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT))
{
Clear();
DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_IGNORE_INSERTS,
FormatMessage(
dwFlags,
NULL,
dwMessageId,
dwLanguageId,
(LPTSTR) &m_text,
0,
NULL);
}
LPTSTR text() const { return m_text; }
operator LPTSTR() const { return text(); }
protected:
LPTSTR m_text;
};
Find a more complete version of the above code here: https://github.com/stephenquan/FormatMessage
With the above class, the usage is simply:
std::wcout << (LPTSTR) CFormatMessage(GetLastError()) << L"\n";
The code below is code is the C++ equivalent I've written out in contrast to Microsoft's ErrorExit() but slightly altered to avoid all macros and use unicode. The idea here is to avoid unnecessary casts and mallocs. I couldn't escape all of the C casts but this is the best I could muster. Pertaining to FormatMessageW(), which requires a pointer to be allocated by the format function and the Error Id from the GetLastError(). The pointer after static_cast can be used like a normal wchar_t pointer.
#include <string>
#include <windows.h>
void __declspec(noreturn) error_exit(const std::wstring FunctionName)
{
// Retrieve the system error message for the last-error code
const DWORD ERROR_ID = GetLastError();
void* MsgBuffer = nullptr;
LCID lcid;
GetLocaleInfoEx(L"en-US", LOCALE_RETURN_NUMBER | LOCALE_ILANGUAGE, (wchar_t*)&lcid, sizeof(lcid));
//get error message and attach it to Msgbuffer
FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, ERROR_ID, lcid, (wchar_t*)&MsgBuffer, 0, NULL);
//concatonate string to DisplayBuffer
const std::wstring DisplayBuffer = FunctionName + L" failed with error " + std::to_wstring(ERROR_ID) + L": " + static_cast<wchar_t*>(MsgBuffer);
// Display the error message and exit the process
MessageBoxExW(NULL, DisplayBuffer.c_str(), L"Error", MB_ICONERROR | MB_OK, static_cast<WORD>(lcid));
ExitProcess(ERROR_ID);
}
Try this:
void PrintLastError (const char *msg /* = "Error occurred" */) {
DWORD errCode = GetLastError();
char *err;
if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPTSTR) &err,
0,
NULL))
return;
static char buffer[1024];
_snprintf(buffer, sizeof(buffer), "ERROR: %s: %s\n", msg, err);
OutputDebugString(buffer); // or otherwise log it
LocalFree(err);
}
This is more an addition to the majority of the answers, but instead of using LocalFree(errorText)
use the HeapFree
function:
::HeapFree(::GetProcessHeap(), NULL, errorText);
From the MSDN site:
Windows 10:
LocalFree is not in the modern SDK, so it cannot be used to free the result buffer. Instead, use HeapFree (GetProcessHeap(), allocatedMessage). In this case, this is the same as calling LocalFree on memory.
Update
I found that LocalFree
is in version 10.0.10240.0 of the SDK (line 1108 in WinBase.h). However, the warning still exists in the link above.
#pragma region Desktop Family or OneCore Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
WINBASEAPI
_Success_(return==0)
_Ret_maybenull_
HLOCAL
WINAPI
LocalFree(
_Frees_ptr_opt_ HLOCAL hMem
);
#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) */
#pragma endregion
Update 2
I would also suggest using the FORMAT_MESSAGE_MAX_WIDTH_MASK
flag to tidy up line breaks in system messages.
From the MSDN site:
FORMAT_MESSAGE_MAX_WIDTH_MASK
The function ignores regular line breaks in the message definition text. The function stores hard-coded line breaks in the message definition text into the output buffer. The function generates no new line breaks.
Update 3
There appears to be 2 particular system error codes that do not return the full message using the recommended approach:
Why does FormatMessage only create partial messages for ERROR_SYSTEM_PROCESS_TERMINATED and ERROR_UNHANDLED_EXCEPTION system errors?