没有:
如何使用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);
}
答案 0 :(得分:127)
这是从系统中为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;
}
这与David Hanak的答案之间的关键区别在于使用FORMAT_MESSAGE_IGNORE_INSERTS
标志。 MSDN对如何使用插入有点不清楚,但在检索系统消息时Raymond Chen notes that you should never use them,因为您无法知道系统期望的插入。
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的直接部分。
答案 1 :(得分:14)
请注意,您无法执行以下操作:
{
LPCTSTR errorText = _com_error(hresult).ErrorMessage();
// do something with the error...
//automatic cleanup when error goes out of scope
}
在堆栈上创建和销毁类时,会将errorText指向无效位置。在大多数情况下,此位置仍将包含错误字符串,但在编写线程应用程序时,该可能性很快就会消失。
所以 始终 按照以上Shog9的说法执行以下操作:
{
_com_error error(hresult);
LPCTSTR errorText = error.ErrorMessage();
// do something with the error...
//automatic cleanup when error goes out of scope
}
答案 2 :(得分:11)
试试这个:
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);
}
答案 3 :(得分:4)
以下是处理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);
}
答案 4 :(得分:4)
这是对大多数答案的补充,但不是使用LocalFree(errorText)
而是使用HeapFree
函数:
::HeapFree(::GetProcessHeap(), NULL, errorText);
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
我还建议使用FORMAT_MESSAGE_MAX_WIDTH_MASK
标志来整理系统消息中的换行符。
<强> FORMAT_MESSAGE_MAX_WIDTH_MASK 强>
该函数忽略消息定义文本中的常规换行符。该函数将消息定义文本中的硬编码换行符存储到输出缓冲区中。该函数不会生成新的换行符。
更新3
似乎有2个特定的系统错误代码没有使用推荐的方法返回完整的消息:
答案 5 :(得分:1)
如其他答案所指出:
FormatMessage
的结果是DWORD
,而不是HRESULT
(通常是GetLastError()
)。LocalFree
才能释放FormatMessage
分配的内存我接受了以上几点,并补充了一些答案:
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;
};
在此处找到上述代码的更完整版本:https://github.com/stephenquan/FormatMessage
在上面的类中,用法很简单:
std::wcout << (LPTSTR) CFormatMessage(GetLastError()) << L"\n";
答案 6 :(得分:1)
从c ++ 11开始,您可以使用标准库代替FormatMessage
:
#include <system_error>
std::string message = std::system_category().message(hr)
答案 7 :(得分:0)
下面的代码是代码是C ++的等价物我写的与Microsoft's ErrorExit()形成对比,但略有改动以避免所有的宏并使用unicode。这里的想法是避免不必要的强制转换和malloc。我无法逃脱所有的C演员,但这是我能鼓起的最好的。与FormatMessageW()有关,它需要一个指针由格式函数和GetLastError()的Error 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);
}