我应该如何在C ++中正确使用FormatMessage()?

没有

  • MFC
  • ATL

我如何使用FormatMessage()获取HRESULT的错误文本?

  HRESULT hresult = application.CreateInstance("Excel.Application"); if (FAILED(hresult)) { // what should i put here to obtain a human-readable // description of the error? exit (hresult); } 

以下是从系统获取HRESULT (在这种情况下命名为hresult,或者可以用GetLastError()replace它GetLastError()的错误消息的正确方法:

 LPTSTR errorText = NULL; FormatMessage( // use system message tables to retrieve error text FORMAT_MESSAGE_FROM_SYSTEM // allocate buffer on local heap for error text |FORMAT_MESSAGE_ALLOCATE_BUFFER // Important! will fail otherwise, since we're not // (and CANNOT) pass insertion parameters |FORMAT_MESSAGE_IGNORE_INSERTS, NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM hresult, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorText, // output 0, // minimum size for output buffer NULL); // arguments - see note if ( NULL != errorText ) { // ... do something with the string `errorText` - log it, display it to the user, etc. // release memory allocated by FormatMessage() LocalFree(errorText); errorText = NULL; } 

这和David Hanak的答案之间的主要区别是使用了FORMAT_MESSAGE_IGNORE_INSERTS标志。 MSDN对于如何使用插入有点不清楚,但是Raymond Chen指出,在检索系统消息时,不应该使用它们 ,因为你无法知道系统期望的插入。

FWIW,如果你使用的是Visual C ++,你可以通过使用_com_error类来让你的生活变得更简单:

 { _com_error error(hresult); LPCTSTR errorText = error.ErrorMessage(); // do something with the error... //automatic cleanup when error goes out of scope } 

据我所知,不是MFC或ATL的一部分。

请记住,您不能执行以下操作:

 { LPCTSTR errorText = _com_error(hresult).ErrorMessage(); // do something with the error... //automatic cleanup when error goes out of scope } 

由于该类在堆栈上创build并销毁,因此将errorText指向无效的位置。 在大多数情况下,这个位置将仍然包含错误string,但是在编写线程应用程序时,这种可能性会快速消失

所以总是按照上面的Shog9回答如下:

 { _com_error error(hresult); LPCTSTR errorText = error.ErrorMessage(); // do something with the error... //automatic cleanup when error goes out of scope } 

尝试这个:

 void PrintLastError (const char *msg /* = "Error occurred" */) { DWORD errCode = GetLastError(); char *err; if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, errCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language (LPTSTR) &err, 0, NULL)) return; static char buffer[1024]; _snprintf(buffer, sizeof(buffer), "ERROR: %s: %s\n", msg, err); OutputDebugString(buffer); // or otherwise log it LocalFree(err); } 

这是处理Unicode的David的函数的一个版本

 void HandleLastError(const TCHAR *msg /* = "Error occured" */) { DWORD errCode = GetLastError(); TCHAR *err; if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, errCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language (LPTSTR) &err, 0, NULL)) return; //TRACE("ERROR: %s: %s", msg, err); TCHAR buffer[1024]; _sntprintf_s(buffer, sizeof(buffer), _T("ERROR: %s: %s\n"), msg, err); OutputDebugString(buffer); LocalFree(err); 

}

这是对大部分答案的补充,但不是使用LocalFree(errorText) ,而是使用HeapFree函数:

 ::HeapFree(::GetProcessHeap(), NULL, errorText); 

从MSDN网站 :

Windows 10
LocalFree不在现代的SDK中,所以不能用来释放结果缓冲区。 相反,使用HeapFree(GetProcessHeap(),allocatedMessage)。 在这种情况下,这与在内存中调用LocalFree相同。

更新
我发现LocalFree在SDK的版本10.0.10240.0(WinBase.h中的1108行)。 但是,上述链接中仍然存在警告。

 #pragma region Desktop Family or OneCore Family #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) WINBASEAPI _Success_(return==0) _Ret_maybenull_ HLOCAL WINAPI LocalFree( _Frees_ptr_opt_ HLOCAL hMem ); #endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) */ #pragma endregion 

更新2
我还build议使用FORMAT_MESSAGE_MAX_WIDTH_MASK标志清理系统消息中的换行符。

从MSDN网站 :

FORMAT_MESSAGE_MAX_WIDTH_MASK
该函数忽略消息定义文本中的常规换行符。 该函数将消息定义文本中的硬编码换行符存储到输出缓冲区中。 该函数不会生成新的换行符。

更新3
似乎有2个特定的系统错误代码不会使用推荐的方法返回完整的消息:

为什么FormatMessage只为ERROR_SYSTEM_PROCESS_TERMINATED和ERROR_UNHANDLED_EXCEPTION系统错误创build部分消息?

下面的代码是代码是C ++相当于我已经写出了对比微软的ErrorExit(),但略有改变,以避免所有的macros和使用Unicode。 这里的想法是避免不必要的转换和mallocs。 我无法逃脱所有的C演员阵容,但这是我所能做的最好的。 与FormatMessageW()有关,它需要一个由格式函数分配的指针和来自GetLastError()的错误ID。 static_cast之后的指针可以像普通的wchar_t指针一样使用。

 #include <string> #include <windows.h> void __declspec(noreturn) error_exit(const std::wstring FunctionName) { // Retrieve the system error message for the last-error code const DWORD ERROR_ID = GetLastError(); void* MsgBuffer = nullptr; LCID lcid; GetLocaleInfoEx(L"en-US", LOCALE_RETURN_NUMBER | LOCALE_ILANGUAGE, (wchar_t*)&lcid, sizeof(lcid)); //get error message and attach it to Msgbuffer FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, ERROR_ID, lcid, (wchar_t*)&MsgBuffer, 0, NULL); //concatonate string to DisplayBuffer const std::wstring DisplayBuffer = FunctionName + L" failed with error " + std::to_wstring(ERROR_ID) + L": " + static_cast<wchar_t*>(MsgBuffer); // Display the error message and exit the process MessageBoxExW(NULL, DisplayBuffer.c_str(), L"Error", MB_ICONERROR | MB_OK, static_cast<WORD>(lcid)); ExitProcess(ERROR_ID); }