我应该如何在 C++ 中正确使用 FormatMessage()?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/455434/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:26:17  来源:igfitidea点击:

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

c++windowserror-handlingformatmessage

提问by Aaron

Without:

没有

  • MFC
  • ATL
  • MFC
  • ATL

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

我如何使用FormatMessage()来获取 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);
 }

回答by Shog9

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()):

这是从系统返回错误消息的正确方法HRESULT(在这种情况下命名为 hresult,或者您可以将其替换为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_INSERTSflag. MSDN is a bit unclear on how insertions should be used, but Raymond Chen notes that you should never use themwhen retrieving a system message, as you've no way of knowing which insertions the system expects.

这与大卫哈纳克的答案之间的主要区别在于FORMAT_MESSAGE_IGNORE_INSERTS旗帜的使用。MSDN 对如何使用插入有点不清楚,但Raymond Chen 指出在检索系统消息时永远不要使用它们,因为您无法知道系统期望哪些插入。

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

FWIW,如果您使用的是 Visual C++,则可以通过使用_com_error该类使您的生活更轻松:

{
   _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.

据我所知,它不是 MFC 或 ATL 的一部分。

回答by Marius

Keep in mind that you cannot do the following:

请记住,您不能执行以下操作:

{
   LPCTSTR errorText = _com_error(hresult).ErrorMessage();

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

As the class is created and destroyed on the stack leaving errorText to point to an invalid location. In most cases this location will still contain the error string, but that likelihood falls away fast when writing threaded applications.

当类在堆栈上创建和销毁时,使 errorText 指向无效位置。在大多数情况下,此位置仍将包含错误字符串,但在编写线程应用程序时,这种可能性会很快消失。

So alwaysdo it as follows as answered by Shog9 above:

因此,请始终按照上面的 Shog9 回答如下操作:

{
   _com_error error(hresult);
   LPCTSTR errorText = error.ErrorMessage();

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

回答by David Hanak

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

回答by Oleg Zhylin

Here is a version of David's function that handles Unicode

这是处理 Unicode 的 David 函数的一个版本

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

}

}

回答by Class Skeleton

This is more an addition to the majority of the answers, but instead of using LocalFree(errorText)use the HeapFreefunction:

这更像是对大多数答案的补充,但不是LocalFree(errorText)使用HeapFree函数:

::HeapFree(::GetProcessHeap(), NULL, errorText);

From the MSDN site:

从 MSDN 站点

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.

Windows 10
LocalFree 不在现代 SDK 中,因此它不能用于释放结果缓冲区。相反,使用 HeapFree(GetProcessHeap()、locatedMessage)。在这种情况下,这与在内存上调用 LocalFree 相同。

Update
I found that LocalFreeis in version 10.0.10240.0 of the SDK (line 1108 in WinBase.h). However, the warning still exists in the link above.

更新
我发现它LocalFree在 SDK 的 10.0.10240.0 版中(WinBase.h 中的第 1108 行)。但是,警告仍然存在于上面的链接中。

#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_MASKflag to tidy up line breaks in system messages.

更新 2
我还建议使用该FORMAT_MESSAGE_MAX_WIDTH_MASK标志来整理系统消息中的换行符。

From the MSDN site:

从 MSDN 站点

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.

FORMAT_MESSAGE_MAX_WIDTH_MASK
该函数忽略消息定义文本中的常规换行符。该函数将消息定义文本中的硬编码换行符存储到输出缓冲区中。该函数不会产生新的换行符。

Update 3
There appears to be 2 particular system error codes that do not return the full message using the recommended approach:

更新 3
似乎有 2 个特定的系统错误代码不会使用推荐的方法返回完整消息:

Why does FormatMessage only create partial messages for ERROR_SYSTEM_PROCESS_TERMINATED and ERROR_UNHANDLED_EXCEPTION system errors?

为什么 FormatMessage 只为 ERROR_SYSTEM_PROCESS_TERMINATED 和 ERROR_UNHANDLED_EXCEPTION 系统错误创建部分消息?

回答by Chronial

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

从 c++11 开始,您可以使用标准库而不是FormatMessage

#include <system_error>

std::string message = std::system_category().message(hr)

回答by Stephen Quan

As pointed out in other answers:

正如其他答案中所指出的:

  • FormatMessagetakes a DWORDresult not a HRESULT(typically GetLastError()).
  • LocalFreeis needed to release memory that was allocated by FormatMessage
  • FormatMessageDWORD结果不是一个HRESULT(典型地GetLastError())。
  • LocalFree需要释放分配的内存 FormatMessage

I took the above points and added a few more for my answer:

我接受了以上几点,并为我的答案添加了更多内容:

  • Wrap the FormatMessagein a class to automatically 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
  • 将 包裹FormatMessage在一个类中以根据需要自动分配和释放内存
  • 使用运算符重载(例如operator LPTSTR() const { return ...; },您的类可以用作字符串
class CFormatMessage
{
public:
    CFormatMessage(DWORD dwError) : m_ErrorText(NULL) { Assign(dwError); }
    ~CFormatMessage() { Clear(); }
    void Clear() { if (m_ErrorText != NULL) { LocalFree(m_ErrorText); m_ErrorText = NULL; } }
    void Assign(DWORD dwError) {
        Clear();
        FormatMessage(
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            dwError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPTSTR)&m_ErrorText,
            0,
            NULL);
    }
    LPTSTR ErrorText() const { return m_ErrorText; }
    operator LPTSTR() const { return ErrorText(); }
protected:
    LPTSTR m_ErrorText;
};

Find a more complete version of the above code here: https://github.com/stephenquan/FormatMessage

在此处找到上述代码的更完整版本:https: //github.com/stephenquan/FormatMessage

With the above class, the usage is simply:

有了上面的类,用法很简单:

    std::wcout << (LPTSTR) CFormatMessage(GetLastError()) << L"\n";

回答by Stephen Quan

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.

下面的代码是我写出的与Microsoft 的 ErrorExit()相反的 C++ 等效代码,但略有改动以避免所有宏并使用 unicode。这里的想法是避免不必要的强制转换和 malloc。我无法逃脱所有的 C 类型转换,但这是我能召集的最好的。与 FormatMessageW() 相关,它需要一个由格式函数分配的指针和来自 GetLastError() 的错误 ID。static_cast 之后的指针可以像普通的 wchar_t 指针一样使用。

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