C++ 如何从GetLastError()返回的错误代码中获取错误消息?

C++ 如何从GetLastError()返回的错误代码中获取错误消息?,c++,c,winapi,C++,C,Winapi,Windows API调用后,如何以文本形式获取最后一条错误消息 GetLastError()返回一个整数值,而不是文本消息。将把的整数返回转换为文本消息。通常,您需要使用FormatMessage将Win32错误代码转换为文本 从MSDN: 格式化消息字符串。功能 需要一个消息定义作为 输入。消息定义可能会出现 从传递到 功能。它可以来自一条信息 表中的表资源已加载 模块。或者打电话的人可以问 函数来搜索系统的 的消息表资源 消息定义。函数查找 消息中的消息定义 基于消息的表资源 标识符和语言

Windows API调用后,如何以文本形式获取最后一条错误消息


GetLastError()
返回一个整数值,而不是文本消息。

将把的整数返回转换为文本消息。

通常,您需要使用
FormatMessage
将Win32错误代码转换为文本

从MSDN:

格式化消息字符串。功能 需要一个消息定义作为 输入。消息定义可能会出现 从传递到 功能。它可以来自一条信息 表中的表资源已加载 模块。或者打电话的人可以问 函数来搜索系统的 的消息表资源 消息定义。函数查找 消息中的消息定义 基于消息的表资源 标识符和语言标识符。 该函数用于复制已格式化的 将消息文本发送到输出缓冲区, 处理任何嵌入式插入 如有要求,请按顺序排列

格式化消息的声明:

DWORD WINAPI FormatMessage(
  __in      DWORD dwFlags,
  __in_opt  LPCVOID lpSource,
  __in      DWORD dwMessageId, // your error code
  __in      DWORD dwLanguageId,
  __out     LPTSTR lpBuffer,
  __in      DWORD nSize,
  __in_opt  va_list *Arguments
);

MSDN有一些示例代码,演示了如何将
FormatMessage()
GetLastError()
结合使用:

更新(2017年11月)以考虑一些注释

简单的例子:

wchar_t buf[256];
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
               NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
               buf, (sizeof(buf) / sizeof(wchar_t)), NULL);
如果您使用的是c#,则可以使用以下代码:

using System.Runtime.InteropServices;

public static class WinErrors
{
    #region definitions
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr LocalFree(IntPtr hMem);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern int FormatMessage(FormatMessageFlags dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, ref IntPtr lpBuffer, uint nSize, IntPtr Arguments);

    [Flags]
    private enum FormatMessageFlags : uint
    {
        FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100,
        FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200,
        FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000,
        FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000,
        FORMAT_MESSAGE_FROM_HMODULE = 0x00000800,
        FORMAT_MESSAGE_FROM_STRING = 0x00000400,
    }
    #endregion

    /// <summary>
    /// Gets a user friendly string message for a system error code
    /// </summary>
    /// <param name="errorCode">System error code</param>
    /// <returns>Error string</returns>
    public static string GetSystemMessage(int errorCode)
    {
        try
        {
            IntPtr lpMsgBuf = IntPtr.Zero;

            int dwChars = FormatMessage(
                FormatMessageFlags.FORMAT_MESSAGE_ALLOCATE_BUFFER | FormatMessageFlags.FORMAT_MESSAGE_FROM_SYSTEM | FormatMessageFlags.FORMAT_MESSAGE_IGNORE_INSERTS,
                IntPtr.Zero,
                (uint) errorCode,
                0, // Default language
                ref lpMsgBuf,
                0,
                IntPtr.Zero);
            if (dwChars == 0)
            {
                // Handle the error.
                int le = Marshal.GetLastWin32Error();
                return "Unable to get error code string from System - Error " + le.ToString();
            }

            string sRet = Marshal.PtrToStringAnsi(lpMsgBuf);

            // Free the buffer.
            lpMsgBuf = LocalFree(lpMsgBuf);
            return sRet;
        }
        catch (Exception e)
        {
            return "Unable to get error code string from System -> " + e.ToString();
        }
    }
}
使用System.Runtime.InteropServices;
公共静态类WinErrors
{
#区域定义
[DllImport(“kernel32.dll”,SetLastError=true)]
静态外部IntPtr LocalFree(IntPtr hMem);
[DllImport(“kernel32.dll”,SetLastError=true)]
静态外部int FormatMessage(FormatMessageFlags dwFlags、IntPtr lpSource、uint dwMessageId、uint dwLanguageId、ref IntPtr lpBuffer、uint nSize、IntPtr参数);
[旗帜]
私有枚举FormatMessageFlags:uint
{
格式化消息分配缓冲区=0x00000100,
格式\消息\忽略\插入=0x00000200,
格式化来自\u系统的\u消息\u=0x00001000,
格式\消息\参数\数组=0x00002000,
格式化来自模块的消息=0x0000800,
格式化来自\u字符串的\u消息\u=0x00000400,
}
#端区
/// 
///获取系统错误代码的用户友好字符串消息
/// 
///系统错误代码
///错误字符串
公共静态字符串GetSystemMessage(int errorCode)
{
尝试
{
IntPtr lpMsgBuf=IntPtr.Zero;
int dwChars=FormatMessage(
FormatMessageFlags.FORMAT_MESSAGE_ALLOCATE_BUFFER | FormatMessageFlags.FORMAT_MESSAGE_FROM_SYSTEM | FormatMessageFlags.FORMAT_MESSAGE_IGNORE_INSERTS,
IntPtr.Zero,
(uint)错误代码,
0,//默认语言
参考lpMsgBuf,
0,
IntPtr.0);
if(dwChars==0)
{
//处理错误。
int le=Marshal.GetLastWin32Error();
返回“无法从系统中获取错误代码字符串-错误”+le.ToString();
}
字符串sRet=Marshal.PtrToStringAnsi(lpMsgBuf);
//释放缓冲区。
lpMsgBuf=LocalFree(lpMsgBuf);
返回sRet;
}
捕获(例外e)
{
返回“无法从系统->获取错误代码字符串”+e.ToString();
}
}
}

如果您需要支持MBCS和Unicode,C64先生的回答还不够。缓冲区必须声明为TCHAR,并强制转换为LPTSTR。请注意,此代码不处理Microsoft附加到错误消息的恼人换行符

CString FormatErrorMessage(DWORD ErrorCode)
{
    TCHAR   *pMsgBuf = NULL;
    DWORD   nMsgLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        reinterpret_cast<LPTSTR>(&pMsgBuf), 0, NULL);
    if (!nMsgLen)
        return _T("FormatMessage fail");
    CString sMsg(pMsgBuf, nMsgLen);
    LocalFree(pMsgBuf);
    return sMsg;
}
返回一个数字错误代码。要获取描述性错误消息(例如,向用户显示),您可以调用:

在C++中,可以通过使用STD::String类:

来大大简化接口。
#include <Windows.h>
#include <system_error>
#include <memory>
#include <string>
typedef std::basic_string<TCHAR> String;

String GetErrorMessage(DWORD dwErrorCode)
{
    LPTSTR psz{ nullptr };
    const DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
                                         | FORMAT_MESSAGE_IGNORE_INSERTS
                                         | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                                       NULL, // (not used with FORMAT_MESSAGE_FROM_SYSTEM)
                                       dwErrorCode,
                                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                       reinterpret_cast<LPTSTR>(&psz),
                                       0,
                                       NULL);
    if (cchMsg > 0)
    {
        // Assign buffer to smart pointer with custom deleter so that memory gets released
        // in case String's c'tor throws an exception.
        auto deleter = [](void* p) { ::LocalFree(p); };
        std::unique_ptr<TCHAR, decltype(deleter)> ptrBuffer(psz, deleter);
        return String(ptrBuffer.get(), cchMsg);
    }
    else
    {
        auto error_code{ ::GetLastError() };
        throw std::system_error( error_code, std::system_category(),
                                 "Failed to retrieve error message string.");
    }
}
#包括
#包括
#包括
#包括
typedef std::基本_字符串;
字符串GetErrorMessage(DWORD dwErrorCode)
{
LPTSTR psz{nullptr};
const DWORD cchMsg=格式化消息(格式化来自系统的消息
|格式化\u消息\u忽略\u插入
|格式化\u消息\u分配\u缓冲区,
NULL,//(不与来自\u系统的格式\u消息\u一起使用)
dwErrorCode,
MAKELANGID(LANG_中立,SUBLANG_默认),
重新解释铸件和psz,
0,
无效);
如果(cchMsg>0)
{
//使用自定义删除器将缓冲区分配给智能指针,以便释放内存
//以防字符串的c'tor引发异常。
自动删除器=[](void*p){::LocalFree(p);};
std::唯一的ptr ptrBuffer(psz,deleter);
返回字符串(ptrBuffer.get(),cchMsg);
}
其他的
{
自动错误代码{::GetLastError()};
抛出标准::系统错误(错误代码,标准::系统类别(),
“检索错误消息字符串失败。”);
}
}
注意:这些函数也适用于HRESULT值。只需将第一个参数从DWORD dwErrorCode更改为HRESULT HRESULT。代码的其余部分可以保持不变


与现有答案相比,这些实现提供了以下改进:
  • 完整的示例代码,而不仅仅是对要调用的API的引用
  • 提供C和C++实现。
  • 适用于Unicode和MBCS项目设置
  • 将错误代码作为输入参数。这一点很重要,因为线程的最后一个错误代码仅在定义良好的点上有效。输入参数允许调用方遵循记录的合同
  • 实现适当的异常安全性。与隐式使用异常的所有其他解决方案不同,此实现在发生异常时不会泄漏内存
    CString FormatErrorMessage(DWORD ErrorCode)
    {
        TCHAR   *pMsgBuf = NULL;
        DWORD   nMsgLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            reinterpret_cast<LPTSTR>(&pMsgBuf), 0, NULL);
        if (!nMsgLen)
            return _T("FormatMessage fail");
        CString sMsg(pMsgBuf, nMsgLen);
        LocalFree(pMsgBuf);
        return sMsg;
    }
    
    CString GetLastErrorString()
    {
        return FormatErrorMessage(GetLastError());
    }
    
    // This functions fills a caller-defined character buffer (pBuffer)
    // of max length (cchBufferLength) with the human-readable error message
    // for a Win32 error code (dwErrorCode).
    // 
    // Returns TRUE if successful, or FALSE otherwise.
    // If successful, pBuffer is guaranteed to be NUL-terminated.
    // On failure, the contents of pBuffer are undefined.
    BOOL GetErrorMessage(DWORD dwErrorCode, LPTSTR pBuffer, DWORD cchBufferLength)
    {
        if (cchBufferLength == 0)
        {
            return FALSE;
        }
    
        DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                     NULL,  /* (not used with FORMAT_MESSAGE_FROM_SYSTEM) */
                                     dwErrorCode,
                                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                     pBuffer,
                                     cchBufferLength,
                                     NULL);
        return (cchMsg > 0);
    }
    
    #include <Windows.h>
    #include <system_error>
    #include <memory>
    #include <string>
    typedef std::basic_string<TCHAR> String;
    
    String GetErrorMessage(DWORD dwErrorCode)
    {
        LPTSTR psz{ nullptr };
        const DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
                                             | FORMAT_MESSAGE_IGNORE_INSERTS
                                             | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                                           NULL, // (not used with FORMAT_MESSAGE_FROM_SYSTEM)
                                           dwErrorCode,
                                           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                           reinterpret_cast<LPTSTR>(&psz),
                                           0,
                                           NULL);
        if (cchMsg > 0)
        {
            // Assign buffer to smart pointer with custom deleter so that memory gets released
            // in case String's c'tor throws an exception.
            auto deleter = [](void* p) { ::LocalFree(p); };
            std::unique_ptr<TCHAR, decltype(deleter)> ptrBuffer(psz, deleter);
            return String(ptrBuffer.get(), cchMsg);
        }
        else
        {
            auto error_code{ ::GetLastError() };
            throw std::system_error( error_code, std::system_category(),
                                     "Failed to retrieve error message string.");
        }
    }
    
    #include <Windows.h>
    
    /***
     * returns 0 if there was enough space, size of buffer in bytes needed
     * to fit the result, if there wasn't enough space. -1 on error.
     */
    __declspec(dllexport)
    int GetErrorMessageA(DWORD dwErrorCode, LPSTR lpResult, DWORD dwBytes)
    {    
        LPSTR tmp;
        DWORD result_len;
    
        result_len = FormatMessageA (
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
            NULL,
            dwErrorCode,
            LANG_SYSTEM_DEFAULT,
            (LPSTR)&tmp,
            0,
            NULL
        );        
    
        if (result_len == 0) {
            return -1;
        }
    
        // FormatMessage's return is 1 character too short.
        ++result_len;
    
        strncpy(lpResult, tmp, dwBytes);
    
        lpResult[dwBytes - 1] = 0;
        LocalFree((HLOCAL)tmp);
    
        if (result_len <= dwBytes) {
            return 0;
        } else {
            return result_len;
        }
    }
    
    /***
     * returns 0 if there was enough space, size of buffer in bytes needed
     * to fit the result, if there wasn't enough space. -1 on error.
     */
    __declspec(dllexport)
    int GetErrorMessageW(DWORD dwErrorCode, LPWSTR lpResult, DWORD dwBytes)
    {   
        LPWSTR tmp;
        DWORD nchars;
        DWORD result_bytes;
    
        nchars = dwBytes >> 1;
    
        result_bytes = 2 * FormatMessageW (
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
            NULL,
            dwErrorCode,
            LANG_SYSTEM_DEFAULT,
            (LPWSTR)&tmp,
            0,
            NULL
        );    
    
        if (result_bytes == 0) {
            return -1;
        } 
    
        // FormatMessage's return is 1 character too short.
        result_bytes += 2;
    
        wcsncpy(lpResult, tmp, nchars);
        lpResult[nchars - 1] = 0;
        LocalFree((HLOCAL)tmp);
    
        if (result_bytes <= dwBytes) {
            return 0;
        } else {
            return result_bytes * 2;
        }
    }
    
    #ifndef GetErrorMessage_H 
    #define GetErrorMessage_H 
    #include <Windows.h>    
    
    /***
     * returns 0 if there was enough space, size of buffer in bytes needed
     * to fit the result, if there wasn't enough space. -1 on error.
     */
    static inline int GetErrorMessageA(DWORD dwErrorCode, LPSTR lpResult, DWORD dwBytes)
    {    
        LPSTR tmp;
        DWORD result_len;
    
        result_len = FormatMessageA (
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
            NULL,
            dwErrorCode,
            LANG_SYSTEM_DEFAULT,
            (LPSTR)&tmp,
            0,
            NULL
        );        
    
        if (result_len == 0) {
            return -1;
        }
    
        // FormatMessage's return is 1 character too short.
        ++result_len;
    
        strncpy(lpResult, tmp, dwBytes);
    
        lpResult[dwBytes - 1] = 0;
        LocalFree((HLOCAL)tmp);
    
        if (result_len <= dwBytes) {
            return 0;
        } else {
            return result_len;
        }
    }
    
    /***
     * returns 0 if there was enough space, size of buffer in bytes needed
     * to fit the result, if there wasn't enough space. -1 on error.
     */
    static inline int GetErrorMessageW(DWORD dwErrorCode, LPWSTR lpResult, DWORD dwBytes)
    {   
        LPWSTR tmp;
        DWORD nchars;
        DWORD result_bytes;
    
        nchars = dwBytes >> 1;
    
        result_bytes = 2 * FormatMessageW (
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
            NULL,
            dwErrorCode,
            LANG_SYSTEM_DEFAULT,
            (LPWSTR)&tmp,
            0,
            NULL
        );    
    
        if (result_bytes == 0) {
            return -1;
        } 
    
        // FormatMessage's return is 1 character too short.
        result_bytes += 2;
    
        wcsncpy(lpResult, tmp, nchars);
        lpResult[nchars - 1] = 0;
        LocalFree((HLOCAL)tmp);
    
        if (result_bytes <= dwBytes) {
            return 0;
        } else {
            return result_bytes * 2;
        }
    }
    
    #endif /* GetErrorMessage_H */
    
    #include <Windows.h>
    #include <Winbase.h>
    #include <assert.h>
    #include <stdio.h>
    
    int main(int argc, char **argv)
    {   
        int (*GetErrorMessageA)(DWORD, LPSTR, DWORD);
        int (*GetErrorMessageW)(DWORD, LPWSTR, DWORD);
        char result1[260];
        wchar_t result2[260];
    
        assert(LoadLibraryA("GetErrorMessageLib.dll"));
    
        GetErrorMessageA = (int (*)(DWORD, LPSTR, DWORD))GetProcAddress (
            GetModuleHandle("GetErrorMessageLib.dll"),
            "GetErrorMessageA"
        );        
        GetErrorMessageW = (int (*)(DWORD, LPWSTR, DWORD))GetProcAddress (
            GetModuleHandle("GetErrorMessageLib.dll"),
            "GetErrorMessageW"
        );        
    
        GetErrorMessageA(33, result1, sizeof(result1));
        GetErrorMessageW(33, result2, sizeof(result2));
    
        puts(result1);
        _putws(result2);
    
        return 0;
    }
    
    #include <stdio.h>
    #include "GetErrorMessage.h"
    #include <stdio.h>
    
    int main(int argc, char **argv)
    {
        char result1[260];
        wchar_t result2[260];
    
        GetErrorMessageA(33, result1, sizeof(result1));
        puts(result1);
    
        GetErrorMessageW(33, result2, sizeof(result2));
        _putws(result2);
    
        return 0;
    }
    
        .global _WinMain@16
    
        .section .text
    _WinMain@16:
        // eax = LoadLibraryA("GetErrorMessageLib.dll")
        push $sz0
        call _LoadLibraryA@4 // stdcall, no cleanup needed
    
        // eax = GetProcAddress(eax, "GetErrorMessageW")
        push $sz1
        push %eax
        call _GetProcAddress@8 // stdcall, no cleanup needed
    
        // (*eax)(errorCode, szErrorMessage)
        push $200
        push $szErrorMessage
        push errorCode       
        call *%eax // cdecl, cleanup needed
        add $12, %esp
    
        push $szErrorMessage
        call __putws // cdecl, cleanup needed
        add $4, %esp
    
        ret $16
    
        .section .rodata
    sz0: .asciz "GetErrorMessageLib.dll"    
    sz1: .asciz "GetErrorMessageW"
    errorCode: .long 33
    
        .section .data
    szErrorMessage: .space 200