92

没有

  • MFC
  • ATL

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

8 回答 8

137

这是从系统返回错误消息的正确方法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;
}

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

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
}

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

于 2009-01-18T17:32:16.423 回答
14

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

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

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

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

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}
于 2009-09-16T22:15:28.663 回答
11

试试这个:

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);
}
于 2009-01-18T16:57:41.313 回答
6

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

#include <system_error>

std::string message = std::system_category().message(hr)
于 2019-06-04T22:59:43.823 回答
5

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

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

从 MSDN 网站

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

更新
我发现它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

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

从 MSDN 网站

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

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

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

于 2015-07-21T13:57:25.693 回答
4

这是处理 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) / sizeof(buffer[0]), _T("ERROR: %s: %s\n"), msg, err);
    OutputDebugString(buffer);
    LocalFree(err);

}

于 2015-02-23T19:36:48.550 回答
2

正如其他答案中指出的那样:

  • FormatMessageDWORD结果不是 a (HRESULT通常是GetLastError())。
  • LocalFree需要释放分配的内存FormatMessage

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

  • 将 包装FormatMessage在一个类中以根据需要分配和释放内存
  • 使用运算符重载(例如operator LPTSTR() const { return ...; },以便您的类可以用作字符串
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;

};

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

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

    std::wcout << (LPTSTR) CFormatMessage(GetLastError()) << L"\n";
于 2019-04-15T01:34:25.080 回答
0

下面的代码是我写出的与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);
}
于 2017-11-15T04:26:16.093 回答