Как правильно использовать FormatMessage () в C++?
без:
- 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);
}
6 ответов:
вот правильный способ получить сообщение об ошибке обратно из системы для
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; }
ключевое различие между этим и ответом Дэвида Ханака заключается в использовании
FORMAT_MESSAGE_IGNORE_INSERTS
флаг. MSDN немного неясно о том, как вставки должны использоваться, но Реймонд Чен отмечает, что вы никогда не должны использовать их при получении системного сообщения, так как вы не можете знать, какие вставки системы рассчитывать.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 }
поскольку класс создается и уничтожается в стеке, оставляя errorText для указания на недопустимое местоположение. В большинстве случаев это место будет по-прежнему содержать строку ошибки, но эта вероятность быстро отпадает при написании потоковых приложений.
Так всегда сделайте это следующим образом, как ответил 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
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(::GetProcessHeap(), NULL, errorText);
Windows 10:
LocalFree не входит в современный SDK, поэтому его нельзя использовать для освобождения буфера результатов. Вместо этого используйте HeapFree (GetProcessHeap(), allocatedMessage). В этом случае это то же самое, что и вызов LocalFree on память.обновление
Я нашел этоLocalFree
находится в версии 10.0.10240.0 SDK (строка 1108 в WinBase.ч.) Однако предупреждение все еще существует в ссылке выше.#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 конкретных кодов системных ошибок, которые не возвращают полное сообщение, используя рекомендуемый подход:
приведенный ниже код является эквивалентом C++, который я написал в отличие от Microsoft ErrorExit () но немного изменен, чтобы избежать всех макросов и использовать unicode. Идея здесь состоит в том, чтобы избежать ненужных бросков и маллоков. Я не мог избежать всех бросков C, но это лучшее, что я мог собрать. Относящийся к FormatMessageW (), который требует, чтобы указатель был выделен функцией format и идентификатором ошибки из GetLastError (). Указатель после 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); }