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

后端 未结 8 733
悲&欢浪女
悲&欢浪女 2020-11-28 03:28

Without:

  • MFC
  • ATL

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

 HRES         


        
相关标签:
8条回答
  • 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);
    

    }

    0 讨论(0)
  • 2020-11-28 04:06

    Since c++11, you can use the standard library instead of FormatMessage:

    #include <system_error>
    
    std::string message = std::system_category().message(hr)
    
    0 讨论(0)
  • 2020-11-28 04:08

    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";
    
    0 讨论(0)
  • 2020-11-28 04:12

    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);
    }
    
    0 讨论(0)
  • 2020-11-28 04:16

    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);
    }
    
    0 讨论(0)
  • 2020-11-28 04:21

    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?

    0 讨论(0)
提交回复
热议问题