How should I use FormatMessage() properly in C++?

Viewed 64773

Without:

  • MFC
  • ATL

How can I use FormatMessage() to get the error text for a HRESULT?

 HRESULT hresult = application.CreateInstance("Excel.Application");

 if (FAILED(hresult))
 {
     // what should i put here to obtain a human-readable
     // description of the error?
     exit (hresult);
 }
8 Answers

Here's the proper way to get an error message back from the system for an HRESULT (named hresult in this case, or you can replace it with GetLastError()):

LPTSTR errorText = NULL;

FormatMessage(
   // use system message tables to retrieve error text
   FORMAT_MESSAGE_FROM_SYSTEM
   // allocate buffer on local heap for error text
   |FORMAT_MESSAGE_ALLOCATE_BUFFER
   // Important! will fail otherwise, since we're not 
   // (and CANNOT) pass insertion parameters
   |FORMAT_MESSAGE_IGNORE_INSERTS,  
   NULL,    // unused with FORMAT_MESSAGE_FROM_SYSTEM
   hresult,
   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
   (LPTSTR)&errorText,  // output 
   0, // minimum size for output buffer
   NULL);   // arguments - see note 
   
if ( NULL != errorText )
{
   // ... do something with the string `errorText` - log it, display it to the user, etc.

   // release memory allocated by FormatMessage()
   LocalFree(errorText);
   errorText = NULL;
}

The key difference between this and David Hanak's answer is the use of the FORMAT_MESSAGE_IGNORE_INSERTS flag. MSDN is a bit unclear on how insertions should be used, but Raymond Chen notes that you should never use them when retrieving a system message, as you've no way of knowing which insertions the system expects.

FWIW, if you're using Visual C++ you can make your life a bit easier by using the _com_error class:

{
   _com_error error(hresult);
   LPCTSTR errorText = error.ErrorMessage();
   
   // do something with the error...

   //automatic cleanup when error goes out of scope
}

Not part of MFC or ATL directly as far as I'm aware.

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);
}

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:

  • Wrap the FormatMessage in a class to allocate and release memory as needed
  • Use operator overload (e.g. operator LPTSTR() const { return ...; } so that your class can be used as a string
class 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);
}
Related