Вот правильный способ получить сообщение об ошибке из системы для HRESULT
(в данном случае с именем hresult, или вы можете заменить его на GetLastError()
):
LPTSTR errorText = NULL;
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM
|FORMAT_MESSAGE_ALLOCATE_BUFFER
|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
hresult,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&errorText,
0,
NULL);
if ( NULL != errorText )
{
LocalFree(errorText);
errorText = NULL;
}
Ключевое различие между этим и ответом Дэвида Ханака - использование FORMAT_MESSAGE_IGNORE_INSERTS
флага. В MSDN немного неясно, как следует использовать вставки, но Рэймонд Чен отмечает, что вы никогда не должны использовать их при получении системного сообщения, поскольку вы не знаете, какие вставки ожидает система.
FWIW, если вы используете Visual C ++, вы можете немного облегчить себе жизнь, используя _com_error
класс:
{
_com_error error(hresult);
LPCTSTR errorText = error.ErrorMessage();
}
Насколько мне известно, не является частью MFC или ATL напрямую.
RegCreateKeyEx
возвращаетLONG
. В его документации сказано, что я могу использовать егоFormatMessage
для получения ошибки, но мне нужно передать файлLONG
в файлHRESULT
.Помните, что вы не можете делать следующее:
{ 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 }
источник
_com_error
объект создается в стеке . Термин, который вы ищете, носит временный характер . В первом примере объект является временным, который уничтожается в конце оператора.std::wstring strErrorText = _com_error(hresult).ErrorMessage();
Попробуй это:
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); }
источник
Это скорее дополнение к большинству ответов, но вместо использования
LocalFree(errorText)
используйтеHeapFree
функцию:::HeapFree(::GetProcessHeap(), NULL, errorText);
С сайта MSDN :
Обновление
Я обнаружил, что
LocalFree
это версия SDK 10.0.10240.0 (строка 1108 в WinBase.h). Однако предупреждение все еще существует по ссылке выше.#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
флаг, чтобы убрать разрывы строк в системных сообщениях.С сайта MSDN :
Обновление 3
Кажется, есть 2 конкретных кода системных ошибок, которые не возвращают полное сообщение с использованием рекомендуемого подхода:
Почему FormatMessage создает только частичные сообщения для системных ошибок ERROR_SYSTEM_PROCESS_TERMINATED и ERROR_UNHANDLED_EXCEPTION?
источник
Начиная с c ++ 11, вы можете использовать стандартную библиотеку вместо
FormatMessage
:#include <system_error> std::string message = std::system_category().message(hr)
источник
Вот версия функции Дэвида, которая обрабатывает 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);
}
источник
_sntprintf_s
в случае UNICODE. Функция принимает число символов, так что вы хотите_countof
илиARRAYSIZE
иначеsizeof(buffer) / sizeof(buffer[0])
вместоsizeof
.Как указано в других ответах:
FormatMessage
беретDWORD
результат не aHRESULT
(обычноGetLastError()
).LocalFree
необходим для освобождения памяти, выделеннойFormatMessage
Я взял вышеупомянутые пункты и добавил еще несколько в свой ответ:
FormatMessage
класс для выделения и освобождения памяти по мере необходимостиoperator LPTSTR() const { return ...; }
чтобы ваш класс можно было использовать как строкуclass CFormatMessage { public: CFormatMessage(DWORD dwMessageId, DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)) : m_text(NULL) { Assign(dwMessageId, dwLanguageId); } ~CFormatMessage() { Clear(); } void Clear() { if (m_text) { LocalFree(m_text); m_text = NULL; } } void Assign(DWORD dwMessageId, DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)) { Clear(); DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, FormatMessage( dwFlags, NULL, dwMessageId, dwLanguageId, (LPTSTR) &m_text, 0, NULL); } LPTSTR text() const { return m_text; } operator LPTSTR() const { return text(); } protected: LPTSTR m_text; };
Более полную версию приведенного выше кода можно найти здесь: https://github.com/stephenquan/FormatMessage
С указанным выше классом использование просто:
std::wcout << (LPTSTR) CFormatMessage(GetLastError()) << L"\n";
источник
Приведенный ниже код является эквивалентом C ++, который я написал, в отличие от Microsoft ErrorExit (), но немного изменен, чтобы избежать использования всех макросов и использовать Unicode. Идея здесь в том, чтобы избежать ненужных приведений и ошибок. Я не мог избежать всех кастингов C, но это лучшее, что я мог собрать. Относится к FormatMessageW (), который требует, чтобы указатель был выделен функцией форматирования и идентификатор ошибки из 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); }
источник