Jak prawidłowo używać FormatMessage() w C++?
Bez :
- MFC
- ATL
Jak mogę używać FormatMessage()
aby uzyskać tekst błędu dla 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 answers
Oto właściwy sposób, aby uzyskać komunikat o błędzie z systemu dla HRESULT
(o nazwie hresult w tym przypadku, lub można go zastąpić 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;
}
Kluczową różnicą między tym a odpowiedzią Davida Hanaka jest użycie flagi FORMAT_MESSAGE_IGNORE_INSERTS
. MSDN jest nieco niejasne, w jaki sposób należy stosować wkładki, ale Raymond Chen zauważa, że nigdy nie należy ich używać podczas pobierania wiadomości systemowej, ponieważ nie wiadomo, których wkładek system oczekuje.
FWIW, jeśli używasz Visual C++ możesz ułatwić sobie życie używając _com_error
klasa:
{
_com_error error(hresult);
LPCTSTR errorText = error.ErrorMessage();
// do something with the error...
//automatic cleanup when error goes out of scope
}
O ile mi wiadomo, nie należy do MFC ani ATL.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-24 07:11:23
Pamiętaj, że nie możesz wykonać następujących czynności:
{
LPCTSTR errorText = _com_error(hresult).ErrorMessage();
// do something with the error...
//automatic cleanup when error goes out of scope
}
Ponieważ klasa jest tworzona i niszczona na stosie, pozostawiając errorText do wskazywania nieprawidłowej lokalizacji. W większości przypadków ta lokalizacja nadal będzie zawierać ciąg błędów, ale prawdopodobieństwo to szybko spada podczas pisania aplikacji z wątkiem.
Więc zawsze zrób to w następujący sposób, jak odpowiedział Shog9 powyżej:
{
_com_error error(hresult);
LPCTSTR errorText = error.ErrorMessage();
// do something with the error...
//automatic cleanup when error goes out of scope
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-16 22:15:28
Spróbuj tego:
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);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-24 07:12:30
Oto wersja funkcji Davida, która obsługuje 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);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-23 19:36:48
Jest to bardziej dodatek do większości odpowiedzi, ale zamiast używać LocalFree(errorText)
Użyj funkcji HeapFree
:
::HeapFree(::GetProcessHeap(), NULL, errorText);
Okna 10:
LocalFree nie jest w nowoczesnym SDK, więc nie może być użyty do uwolnienia bufora wynikowego. Zamiast tego użyj HeapFree(GetProcessHeap (), allocatedMessage). W tym przypadku jest to to samo, co wywołanie LocalFree w pamięci.
Update
Znalazłem, że LocalFree
jest w wersji 10.0.10240.0 SDK (linia 1108 w WinBase.h). Jednak Ostrzeżenie nadal istnieje w linku powyżej.
#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
Sugerowałbym również użycie flagi FORMAT_MESSAGE_MAX_WIDTH_MASK
do uporządkowania podziałów linii w komunikatach systemowych.
FORMAT_MESSAGE_MAX_WIDTH_MASK
Funkcja ignoruje regularne podziały wierszy w tekście definicji wiadomości. Funkcja przechowuje zakodowane na twardo podziały wierszy w tekście definicji wiadomości do bufora wyjściowego. Funkcja nie generuje żadnych nowych podziałów linii.
Update 3
Wydaje się, że istnieją 2 szczególne kody błędów systemu, które nie zwracają pełnej wiadomości przy użyciu zalecanego podejścia:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:07
Poniższy kod jest odpowiednikiem C++, który napisałem w przeciwieństwie do Microsoft ErrorExit () , ale nieco zmieniony, aby uniknąć wszystkich makr i używać unicode. Chodzi o to, aby uniknąć niepotrzebnych odlewów i malloków. Nie mogłem uciec od wszystkich rzutów C, ale to najlepsze, co mogłem zebrać. Odnosi się do metody FormatMessageW (), która wymaga, aby wskaźnik został przydzielony przez funkcję format i identyfikator błędu z GetLastError (). Wskaźnik po static_cast może być użyty jak normalny wskaźnik wschar_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);
}