Oto właściwy sposób na odzyskanie komunikatu o błędzie z systemu HRESULT
(w tym przypadku o nazwie hresult lub możesz go zastąpić 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;
}
Kluczową różnicą między tym a odpowiedzią Davida Hanaka jest użycie FORMAT_MESSAGE_IGNORE_INSERTS
flagi. MSDN nie jest do końca jasne, w jaki sposób powinny być używane wstawki, ale Raymond Chen zauważa, że nigdy nie należy ich używać podczas pobierania wiadomości systemowej, ponieważ nie można dowiedzieć się, jakich wstawek oczekuje system.
FWIW, jeśli używasz Visual C ++, możesz trochę ułatwić sobie życie, używając _com_error
klasy:
{
_com_error error(hresult);
LPCTSTR errorText = error.ErrorMessage();
}
O ile wiem, nie jest częścią MFC ani ATL bezpośrednio.
RegCreateKeyEx
zwraca aLONG
. Jego dokumentacja mówi, że mogę użyćFormatMessage
do odzyskania błędu, ale muszę rzucićLONG
na plikHRESULT
.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 }
Gdy klasa jest tworzona i niszczona na stosie, pozostawiając errorText wskazujący nieprawidłową lokalizację. W większości przypadków ta lokalizacja nadal będzie zawierać ciąg błędu, ale prawdopodobieństwo to szybko spada podczas pisania aplikacji z wątkami.
Więc zawsze rób to w następujący sposób, zgodnie z odpowiedzią Shog9 powyżej:
{ _com_error error(hresult); LPCTSTR errorText = error.ErrorMessage(); // do something with the error... //automatic cleanup when error goes out of scope }
źródło
_com_error
Obiekt jest tworzony na stosie w obu swoich przykładach. Termin, którego szukasz, jest tymczasowy . W poprzednim przykładzie obiekt jest tymczasowy, który jest niszczony na końcu instrukcji.std::wstring strErrorText = _com_error(hresult).ErrorMessage();
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); }
źródło
Jest to bardziej dodatek do większości odpowiedzi, ale zamiast
LocalFree(errorText)
używaćHeapFree
funkcji:::HeapFree(::GetProcessHeap(), NULL, errorText);
Z witryny MSDN :
Aktualizacja
znalazłem
LocalFree
w wersji 10.0.10240.0 SDK (wiersz 1108 w WinBase.h). Jednak w powyższym linku nadal istnieje ostrzeżenie.#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
Zaktualizuj 2
Sugerowałbym również użycie
FORMAT_MESSAGE_MAX_WIDTH_MASK
flagi do uporządkowania podziałów wierszy w komunikatach systemowych.Z witryny MSDN :
Zaktualizuj 3
Wydaje się, że istnieją 2 konkretne kody błędów systemu, które nie zwracają pełnego komunikatu przy zastosowaniu zalecanego podejścia:
Dlaczego FormatMessage tworzy tylko częściowe komunikaty dla błędów systemowych ERROR_SYSTEM_PROCESS_TERMINATED i ERROR_UNHANDLED_EXCEPTION?
źródło
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);
}
źródło
_sntprintf_s
w przypadku UNICODE. Funkcja przyjmuje liczbę znaków, więc chcesz_countof
lubARRAYSIZE
akasizeof(buffer) / sizeof(buffer[0])
zamiastsizeof
.Od C ++ 11 możesz użyć standardowej biblioteki zamiast
FormatMessage
:#include <system_error> std::string message = std::system_category().message(hr)
źródło
Jak wskazano w innych odpowiedziach:
FormatMessage
przyjmujeDWORD
wynik, a nieHRESULT
(zazwyczajGetLastError()
).LocalFree
jest potrzebny do zwolnienia pamięci przydzielonej przezFormatMessage
Wziąłem powyższe punkty i dodałem kilka więcej do mojej odpowiedzi:
FormatMessage
w klasie, aby przydzielić i zwolnić pamięć w razie potrzebyoperator LPTSTR() const { return ...; }
Twoja klasa mogła być używana jako łańcuchclass 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; };
Znajdź pełniejszą wersję powyższego kodu tutaj: https://github.com/stephenquan/FormatMessage
W przypadku powyższej klasy użycie jest po prostu:
std::wcout << (LPTSTR) CFormatMessage(GetLastError()) << L"\n";
źródło
Poniższy kod jest odpowiednikiem C ++, który napisałem w przeciwieństwie do Microsoft ErrorExit (), ale nieznacznie zmieniony, aby uniknąć wszystkich makr i używać Unicode. Chodzi o to, aby uniknąć niepotrzebnych odlewów i młotków. Nie mogłem uciec od wszystkich rzutów C, ale to najlepsze, na co mogłem się zdobyć. Odnosi się do FormatMessageW (), który wymaga, aby wskaźnik został przydzielony przez funkcję format i identyfikator błędu z GetLastError (). Wskaźnik po static_cast może być używany jak zwykły wskaźnik 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); }
źródło