在Windows API调用之后,如何以文本形式获取最后一条错误消息?
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; }
更新时间(11月13日),以考虑一些意见.
简单的例子:
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);
MSDN有一些示例代码,演示如何使用FormatMessage()
和GetLastError()
一起使用:检索Last-Error代码
FormatMessage会将GetLastError的整数返回变为文本消息.
通常,您需要使用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 );
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#include #include #include typedef std::basic_string 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 (&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 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标志的重要性.
与其他一些答案不同,正确的错误处理/错误报告会默默地忽略错误.
如果您使用的是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 ////// Gets a user friendly string message for a system error code /// /// System error code ///Error string 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(); } } }