如何从GetLastError()返回的错误代码中获取错误消息?
在Windows API调用之后,如何才能以文本forms获取最后的错误消息?
GetLastError()
返回一个整数值,而不是文本消息。
谢谢。
//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; }
简单的例子:
wchar_t buf[256]; FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL);
MSDN有一些示例代码演示了如何一起使用FormatMessage()
和GetLastError()
: 检索最后错误代码
FormatMessage将把GetLastError的整数返回成文本消息。
通常,您需要使用FormatMessage
将Win32错误代码转换为文本。
从MSDN 文档 :
格式化消息string。 该函数需要消息定义作为input。 消息定义可以来自传递给函数的缓冲区。 它可以来自已经加载的模块中的消息表资源。 或者调用者可以要求函数在系统的消息表资源中search消息定义。 该函数根据消息标识符和语言标识符在消息表资源中查找消息定义。 该函数将格式化的消息文本复制到输出缓冲区,处理任何embedded的插入序列(如果请求的话)。
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 );
如果你使用的是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(); } } }
如果你需要支持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()); }
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); } }
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 <exception> #include <stdexcept> #include <memory> #include <string> typedef std::basic_string<TCHAR> String; String GetErrorMessage(DWORD dwErrorCode) { LPTSTR psz = NULL; 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) { ::HeapFree(::GetProcessHeap(), 0, p); }; std::unique_ptr<TCHAR, decltype(deleter)> ptrBuffer(psz, deleter); return String(ptrBuffer.get(), cchMsg); } else { throw std::runtime_error("Failed to retrieve error message string."); } }
注意:这些函数也适用于HRESULT值。 只要将第一个参数从DWORD dwErrorCode改为HRESULT hResult即可。 其余的代码可以保持不变。
这些实现在现有答案上提供了以下改进:
- 完整的示例代码,不仅仅是对API调用的引用。
- 提供C和C ++实现。
- 适用于Unicode和MBCS项目设置。
- 将错误代码作为input参数。 这很重要,因为线程的最后一个错误代码只在定义好的点上有效。 input参数允许调用者遵循logging的合同。
- 实施适当的例外安全。 与所有隐式使用exception的其他解决scheme不同,如果在构造返回值时引发exception,则此实现不会泄漏内存。
- 正确使用
FORMAT_MESSAGE_IGNORE_INSERTS
标志。 有关更多信息,请参阅FORMAT_MESSAGE_IGNORE_INSERTS标志的重要性 。 - 正确的error handling/错误报告,不像其他一些答案,它默默地忽略错误。
这个答案已经从堆栈溢出文档合并。 下面的用户对这个例子有所贡献: stackptr , Ajay , Cody Gray , IInspectable 。
我会离开这里,因为我需要以后使用它。 它是一个小的二进制兼容工具的源代码,在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 */
dynamic用例(假设错误代码是有效的,否则需要-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.