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


137

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

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


在Visual Studio的“工具”部分中曾经有一个exe错误查找,当您只需要来自错误的消息进行调试时,这样做会做得很好。
ColdCat 2014年

@ColdCat:对于调试,只需添加一个@err,hr监视程序,并使调试器将最后的错误代码自动转换为人类可读的表示,就会容易得多。该,hr格式说明适用于任何表达式,其值的积分值,如5,hr表会显示“ERROR_ACCESS_DENIED:访问被拒绝。”
IInspectable '17

2
GetLastError()文档中:“ 要获取系统错误代码的错误字符串,请使用此FormatMessage()函数。 ”。请参阅MSDN 上的检索上一个错误代码”示例。
雷米·勒博

Answers:


145
//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
std::string GetLastErrorAsString()
{
    //Get the error message, if any.
    DWORD errorMessageID = ::GetLastError();
    if(errorMessageID == 0)
        return std::string(); //No error message has been recorded

    LPSTR messageBuffer = nullptr;
    size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                 NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);

    std::string message(messageBuffer, size);

    //Free the buffer.
    LocalFree(messageBuffer);

    return message;
}

2
我相信您实际上需要(LPSTR)&messageBuffer在这种情况下通过,否则,FormatMessageA不可能更改其值以指向分配的缓冲区。
Kylotan 2014年

2
哦,哇,是的,这很奇怪。它将如何修改指针?但是将指针的地址(指针到指针)传递给它,却将其强制转换为常规指针... Win32怪异。感谢您的注意,将其固定在我自己的代码库(和我的答案)中。非常微妙的收获。
Jamin Gray

1
非常感谢您,您的示例比MSDN中的示例更加清晰。而且,来自MSDN的代码甚至无法编译。它包含一些strsafe.h根本不安全的标头,它在winuser.h和中引起了许多编译器错误winbase.h
Hi-Angel

2
不支持某些错误ID。例如,0x2EE7,ERROR_INTERNET_NAME_NOT_RESOLVED在调用FormatMessage:0x13D时会导致新错误,系统无法在%2的消息文件中找到消息编号0x%1的消息文本。
布伦特

3
此实现的问题:1 GetLastError可能被称为为时已晚。2不支持Unicode。3在没有实施例外安全保证的情况下使用例外。
IInspectable '17

65

已更新(11/2017)以考虑一些评论。

简单的例子:

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

3
@ Hi-Angel-该示例假定您使用定义的UNICODE进行编译。“ FormatMessage”实际上是一个宏,根据应用程序的编译方式,它可以扩展为用于Ansi / MBCS字符缓冲区的“ FormatMessageA”或用于UTF16 / UNICODE缓冲区的“ FormatMessageW”。我随意编辑上面的示例以显式调用与输出缓冲区类型(wchar_t)匹配的版本。
2015年

2
添加FORMAT_MESSAGE_IGNORE_INSERTS:“如果您不受格式字符串的控制,则必须传递FORMAT_MESSAGE_IGNORE_INSERTS以防止%1引起麻烦。” blogs.msdn.microsoft.com/oldnewthing/20071128-00/?p=24353
罗伊·丹顿

1
此实现的问题:1无法指定重要FORMAT_MESSAGE_IGNORE_INSERTS标志。2 GetLastError可能为时已晚。3消息的任意限制为256个代码单元。4没有错误处理。
IInspectable '17

1
sizeof(buf)应该为ARRAYSIZE(buf),因为FormatMessage期望缓冲区的大小以TCHAR为单位,而不是以字节为单位。
Kai

1
我们不能使用256代替代替(sizeof(buf) / sizeof(wchar_t)吗?它可以接受并且安全吗?
BattleTested



14

GetLastError返回数字错误代码。要获取描述性错误消息(例如,显示给用户),可以调用FormatMessage

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

在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.");
    }
}

注意:这些函数也适用于HRESULT值。只需将第一个参数从DWORD dwErrorCode更改为HRESULT hResult。其余代码可以保持不变。


这些实现对现有答案提供了以下改进:

  • 完整的示例代码,而不仅仅是对要调用的API的引用。
  • 提供C和C ++实现。
  • 适用于Unicode和MBCS项目设置。
  • 将错误代码作为输入参数。这很重要,因为线程的最后一个错误代码仅在定义明确的点有效。输入参数允许呼叫者遵循已记录的合同。
  • 实现适当的异常安全性。与所有其他隐式使用异常的解决方案不同,此构造不会在构造返回值时引发异常的情况下泄漏内存。
  • 正确使用FORMAT_MESSAGE_IGNORE_INSERTS标志。有关更多信息,请参见FORMAT_MESSAGE_IGNORE_INSERTS标志的重要性
  • 与其他一些答案不同,正确的错误处理/错误报告可以静默地忽略错误。


该答案已从Stack Overflow文档中合并。以下用户对此示例做出了贡献:stackptrAjayCody Gray♦IInspectable


1
std::runtime_error我建议不要抛出,而是将失败调用后的返回值丢到std::system_error(lastError, std::system_category(), "Failed to retrieve error message string.")哪里。lastErrorGetLastError()FormatMessage()
zett42 '17

FormatMessage直接使用C版本的优势是什么?
Micha Wiedenmann,

@mic:就像问:“ C函数的优点是什么?” 函数通过提供抽象来降低复杂性。在这种情况下,抽象将参数的数量从7个减少到3个,通过传递兼容的标志和参数来实现API的书面合同,并对编码的错误报告逻辑进行编码。它提供了一个简洁的界面,具有易于猜测的语义,使您无需花11分钟时间阅读文档
IInspectable

我喜欢这个答案,很显然,已经对代码的正确性进行了认真的注意。我有两个问题。1)为什么::HeapFree(::GetProcessHeap(), 0, p)在删除器中使用而不是::LocalFree(p)文档中所建议的?2)我意识到文档说可以这样做,但是没有reinterpret_cast<LPTSTR>(&psz)违反严格的别名规则吗?
Teh JoE

@teh:谢谢您的反馈。问题1):老实说,我不知道这是否安全。我不记得HeapFree引入了谁或为什么调用此方法,尽管这仍然是SO Documentation中的主题。我将不得不进行调查(尽管文档似乎很清楚,但这并不安全)。问题2):这有两个方面。对于MBCS配置,LPTSTR是的别名char*。那演员总是安全的。对于Unicode配置,wchar_t*无论如何,投射到都是腥的。我不知道这在C语言中是否安全,但很可能在C ++中不是。我也必须对此进行调查。
IInspectable

13

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

从MSDN 文档中

格式化消息字符串。该功能需要消息定义作为输入。消息定义可以来自传递给函数的缓冲区。它可以来自已加载模块中的消息表资源。或者,调用者可以要求函数在系统的消息表资源中搜索消息定义。该函数根据消息标识符和语言标识符在消息表资源中找到消息定义。该函数将格式化的消息文本复制到输出缓冲区,如果需要,则处理所有嵌入的插入序列。

FormatMessage的声明:

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

7

如果您使用的是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();
        }
    }
}

我确认此代码有效。TS不应该接受这个答案吗?
swdev 2014年

2
如果有必要再投掷还有一个更简单的方法来做到这一点在C#与Win32Exception
SERG

2
@swdev:为什么有人应该用C#回答标记为cc ++的问题?就目前而言,这个答案甚至都没有解决所问的问题。
IInspectable '17

1
好吧,我不记得已经对这个建议的答案进行了投票。我解决了@swdev逻辑中的明显缺陷。但是,由于您不会相信我,因此我现在向您证明:在这里,请再投票。那是我的,因为这个答案-尽管对于一个不同的问题可能有用,但对于所提出的问题来说根本没有用。
IInspectable '17

2
“我想您可以提供显而易见的有用见解” - 确实,我有
IInspectable '17

4

如果您需要支持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;
}

另外,为简便起见,我发现以下方法很有用:

CString GetLastErrorString()
{
    return FormatErrorMessage(GetLastError());
}

万一CStringc'tor抛出异常,此实现会泄漏对的调用分配的内存FormatMessage
IInspectable '17

是的,但是我已经使用了很多年了,这从来都不是问题。CString ctor可能抛出的唯一情况是分配内存失败,并且大多数MFC代码(包括Microsoft提供的东西)都无法如您所愿地处理内存不足的情况。幸运的是,现在大多数PC都具有如此大的内存,因此您必须非常努力地使用所有内存。生成临时CString实例(包括返回CString)的任何用法都存在这种风险。这是风险/便利性的折衷。同样,如果它发生在消息处理程序中,则将捕获异常。
受害人休闲

抱歉,大多数此评论是错误的。“这没发生在我身上”是该死的弱点,尤其是当您知道代码如何失败时。内存量也不会影响分配给进程的可用地址空间。RAM只是性能优化。当编写允许NRVO的代码时,复制删除阻止分配临时文件。消息处理程序中是否捕获到异常取决于进程的位数和外部设置。我提交的答案表明,风险管理不会带来不便。
IInspectable '17

此外,在构建临时文件时内存不足的风险尚无定论。到那时,所有资源都已释放,并且不会有任何不好的结果。
IInspectable '17

1
不,对不起,代码对您没有用。但是TBH在我的问题清单上还很少。
受害人休闲

3
void WinErrorCodeToString(DWORD ErrorCode, string& Message)
{
char* locbuffer = NULL;
DWORD count = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, ErrorCode,
    0, (LPSTR)&locbuffer, 0, nullptr);
if (locbuffer)
{
    if (count)
    {
        int c;
        int back = 0;
        //
        // strip any trailing "\r\n"s and replace by a single "\n"
        //
        while (((c = *CharPrevA(locbuffer, locbuffer + count)) == '\r') ||
            (c == '\n')) {
            count--;
            back++;
        }

        if (back) {
            locbuffer[count++] = '\n';
            locbuffer[count] = '\0';
        }

        Message = "Error: ";
        Message += locbuffer;
    }
    LocalFree(locbuffer);
}
else
{
    Message = "Unknown error code: " + to_string(ErrorCode);
}
}

您还可以添加一些解释吗?
罗伯特

1
此实现的问题:1不支持Unicode。2错误消息的格式不正确。如果调用方需要处理返回的字符串,则可以执行此操作。您的实现使调用者无法选择。3使用例外,但缺乏适当的例外安全性。万一std::string操作员抛出异常,则由分配的缓冲区将FormatMessage泄漏。4为什么不简单地返回a std::string而不是让调用者通过引用传递对象呢?
IInspectable '17

1

从c ++ 11开始,您可以使用标准库代替FormatMessage

#include <system_error>

std::string GetLastErrorAsString(){
    DWORD errorMessageID = ::GetLastError();
    if (errorMessageID == 0) {
        return std::string(); //No error message has been recorded
    } else {
        return std::system_category().message(errorMessageID);
    }
}

只有一个很小的窗口可以在其中GetLastError产生有意义的结果。使用C ++,这里唯一安全的选择是让调用者提供最新的错误代码。给出的代码调用GetLastError 两次肯定没有帮助。同样,尽管方便,但C ++固有的缺乏宽字符支持的功能未能使该error_category接口具有通用性。这只会增加C ++错失良机的悠久历史。
IInspectable

关于对的无用调用的要点GetLastError。但是我看不到在GetLastError这里打电话还是让呼叫者打电话。关于C ++和wchar:不要放弃希望,Microsoft开始允许应用程序仅使用UTF-8
慢性

“我认为没有区别” -考虑以下呼叫站点:log_error("error", GetLastErrorAsString());。还要考虑log_error第一个参数是类型std::string。(不可见的)转换调用只是放弃了您的保证,即从GetLastError您调用它的那一刻开始捕获有意义的值。
IInspectable

您为什么认为std :: string构造函数调用Win32函数?
慢性

1
malloc调用HeapAlloc进程堆。进程堆是可增长的。如果需要增长,它将最终调用VirtualAlloc,它设置调用线程的最后一个错误代码。现在,这完全没有意义了:C ++是一个雷区。通过为接口提供隐含的保证,该实现只是增加了它根本无法辜负的义务。如果您认为没有问题,那么应该容易证明拟议解决方案的正确性。祝好运。
IInspectable

0

我将其留在这里,因为以后需要使用它。它是一个小型二进制兼容工具的源代码,该工具在汇编语言,C和C ++中都可以很好地工作。

GetErrorMessageLib.c(编译为GetErrorMessageLib.dll)

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

内联版本(GetErrorMessage.h):

#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 */

动态用例(假设错误代码有效,否则需要进行-1检查):

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

常规用例(假设错误代码有效,否则需要-1返回检查):

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

与MinGW32中的程序集gnu一起使用的示例(同样,假定错误代码有效,否则需要-1检查)。

    .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

结果: The process cannot access the file because another process has locked a portion of the file.


1
这确实没有添加任何有用的东西。最重要的是,它FormatMessage无缘无故地调用的ANSI版本,并且再无故地将其自身限制为80个字符。恐怕这没有帮助。
IInspectable '17

您是对的,我希望没人能注意到缺少Unicode版本的情况。我将检查如何在gnu中定义Unicode字符串并修改解决方案。对不诚实表示抱歉。
德米特里(Dmitry)

好的unicode版本到了。这不是武断的原因;所有错误消息均少于80个字符,或者不值得阅读,并且错误代码比错误消息更重要。没有超过80个字符的标准错误消息,因此这是一个安全的假设,如果不是,则不会泄漏内存。
德米特里(Dmitry)

3
“所有错误消息均少于80个字符,或者不值得阅读” -是的。ERROR_LOCK_VIOLATION(33)的错误消息是:“该进程无法访问文件,因为另一个进程已锁定了文件的一部分。” 如果您尝试解决问题并在诊断日志文件中找到该字符,则它们显然都超过80个字符,并且非常值得阅读。与现有答案相比,该答案没有任何实质性价值。
IInspectable '17

这同样是幼稚的。它只是很少失败。除了与缓冲区大小有关的程序集实现外(声称只有200个字符时可以容纳200个字符)。同样,这并没有增加任何实质性内容,其他任何答案中都没有。特别是,它比这个答案还要糟糕。在我刚刚指出的问题之上,请参阅此处的项目符号列表,并观察您建议的实现存在哪些问题。
IInspectable '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.