我应该如何在 C++ 中正确使用 FormatMessage()?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/455434/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How should I use FormatMessage() properly in C++?
提问by Aaron
Without:
没有:
- MFC
- ATL
- MFC
- ATL
How can I use FormatMessage()
to get the error text for a HRESULT
?
我如何使用FormatMessage()
来获取 a 的错误文本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);
}
回答by Shog9
Here's the proper way to get an error message back from the system for an HRESULT
(named hresult in this case, or you can replace it with GetLastError()
):
这是从系统返回错误消息的正确方法HRESULT
(在这种情况下命名为 hresult,或者您可以将其替换为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;
}
The key difference between this and David Hanak's answer is the use of the FORMAT_MESSAGE_IGNORE_INSERTS
flag. MSDN is a bit unclear on how insertions should be used, but Raymond Chen notes that you should never use themwhen retrieving a system message, as you've no way of knowing which insertions the system expects.
这与大卫哈纳克的答案之间的主要区别在于FORMAT_MESSAGE_IGNORE_INSERTS
旗帜的使用。MSDN 对如何使用插入有点不清楚,但Raymond Chen 指出在检索系统消息时永远不要使用它们,因为您无法知道系统期望哪些插入。
FWIW, if you're using Visual C++ you can make your life a bit easier by using the _com_error
class:
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
}
Not part of MFC or ATL directly as far as I'm aware.
据我所知,它不是 MFC 或 ATL 的一部分。
回答by Marius
Keep in mind that you cannot do the following:
请记住,您不能执行以下操作:
{
LPCTSTR errorText = _com_error(hresult).ErrorMessage();
// do something with the error...
//automatic cleanup when error goes out of scope
}
As the class is created and destroyed on the stack leaving errorText to point to an invalid location. In most cases this location will still contain the error string, but that likelihood falls away fast when writing threaded applications.
当类在堆栈上创建和销毁时,使 errorText 指向无效位置。在大多数情况下,此位置仍将包含错误字符串,但在编写线程应用程序时,这种可能性会很快消失。
So alwaysdo it as follows as answered by Shog9 above:
因此,请始终按照上面的 Shog9 回答如下操作:
{
_com_error error(hresult);
LPCTSTR errorText = error.ErrorMessage();
// do something with the error...
//automatic cleanup when error goes out of scope
}
回答by David Hanak
Try this:
尝试这个:
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);
}
回答by Oleg Zhylin
Here is a version of David's function that handles Unicode
这是处理 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);
}
}
回答by Class Skeleton
This is more an addition to the majority of the answers, but instead of using LocalFree(errorText)
use the HeapFree
function:
这更像是对大多数答案的补充,但不是LocalFree(errorText)
使用HeapFree
函数:
::HeapFree(::GetProcessHeap(), NULL, errorText);
Windows 10:
LocalFree is not in the modern SDK, so it cannot be used to free the result buffer. Instead, use HeapFree (GetProcessHeap(), allocatedMessage). In this case, this is the same as calling LocalFree on memory.
Windows 10:
LocalFree 不在现代 SDK 中,因此它不能用于释放结果缓冲区。相反,使用 HeapFree(GetProcessHeap()、locatedMessage)。在这种情况下,这与在内存上调用 LocalFree 相同。
Update
I found that LocalFree
is in version 10.0.10240.0 of the SDK (line 1108 in WinBase.h). However, the warning still exists in the link above.
更新
我发现它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
Update 2
I would also suggest using the FORMAT_MESSAGE_MAX_WIDTH_MASK
flag to tidy up line breaks in system messages.
更新 2
我还建议使用该FORMAT_MESSAGE_MAX_WIDTH_MASK
标志来整理系统消息中的换行符。
FORMAT_MESSAGE_MAX_WIDTH_MASK
The function ignores regular line breaks in the message definition text. The function stores hard-coded line breaks in the message definition text into the output buffer. The function generates no new line breaks.
FORMAT_MESSAGE_MAX_WIDTH_MASK
该函数忽略消息定义文本中的常规换行符。该函数将消息定义文本中的硬编码换行符存储到输出缓冲区中。该函数不会产生新的换行符。
Update 3
There appears to be 2 particular system error codes that do not return the full message using the recommended approach:
更新 3
似乎有 2 个特定的系统错误代码不会使用推荐的方法返回完整消息:
为什么 FormatMessage 只为 ERROR_SYSTEM_PROCESS_TERMINATED 和 ERROR_UNHANDLED_EXCEPTION 系统错误创建部分消息?
回答by Chronial
Since c++11, you can use the standard library instead of FormatMessage
:
从 c++11 开始,您可以使用标准库而不是FormatMessage
:
#include <system_error>
std::string message = std::system_category().message(hr)
回答by Stephen Quan
As pointed out in other answers:
正如其他答案中所指出的:
FormatMessage
takes aDWORD
result not aHRESULT
(typicallyGetLastError()
).LocalFree
is needed to release memory that was allocated byFormatMessage
FormatMessage
取DWORD
结果不是一个HRESULT
(典型地GetLastError()
)。LocalFree
需要释放分配的内存FormatMessage
I took the above points and added a few more for my answer:
我接受了以上几点,并为我的答案添加了更多内容:
- Wrap the
FormatMessage
in a class to automatically allocate and release memory as needed - Use operator overload (e.g.
operator LPTSTR() const { return ...; }
so that your class can be used as a string
- 将 包裹
FormatMessage
在一个类中以根据需要自动分配和释放内存 - 使用运算符重载(例如
operator LPTSTR() const { return ...; }
,您的类可以用作字符串
class CFormatMessage
{
public:
CFormatMessage(DWORD dwError) : m_ErrorText(NULL) { Assign(dwError); }
~CFormatMessage() { Clear(); }
void Clear() { if (m_ErrorText != NULL) { LocalFree(m_ErrorText); m_ErrorText = NULL; } }
void Assign(DWORD dwError) {
Clear();
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&m_ErrorText,
0,
NULL);
}
LPTSTR ErrorText() const { return m_ErrorText; }
operator LPTSTR() const { return ErrorText(); }
protected:
LPTSTR m_ErrorText;
};
Find a more complete version of the above code here: https://github.com/stephenquan/FormatMessage
在此处找到上述代码的更完整版本:https: //github.com/stephenquan/FormatMessage
With the above class, the usage is simply:
有了上面的类,用法很简单:
std::wcout << (LPTSTR) CFormatMessage(GetLastError()) << L"\n";
回答by Stephen Quan
The code below is code is the C++ equivalent I've written out in contrast to Microsoft's ErrorExit()but slightly altered to avoid all macros and use unicode. The idea here is to avoid unnecessary casts and mallocs. I couldn't escape all of the C casts but this is the best I could muster. Pertaining to FormatMessageW(), which requires a pointer to be allocated by the format function and the Error Id from the GetLastError(). The pointer after static_cast can be used like a normal wchar_t pointer.
下面的代码是我写出的与Microsoft 的 ErrorExit()相反的 C++ 等效代码,但略有改动以避免所有宏并使用 unicode。这里的想法是避免不必要的强制转换和 malloc。我无法逃脱所有的 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);
}