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