Esta es la forma correcta de obtener un mensaje de error del sistema para un HRESULT
(llamado hresult en este caso, o puede reemplazarlo por 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;
}
La diferencia clave entre esto y la respuesta de David Hanak es el uso de la FORMAT_MESSAGE_IGNORE_INSERTS
bandera. MSDN no tiene claro cómo se deben usar las inserciones, pero Raymond Chen señala que nunca debe usarlas al recuperar un mensaje del sistema, ya que no tiene forma de saber qué inserciones espera el sistema.
FWIW, si está usando Visual C ++, puede hacer su vida un poco más fácil usando la _com_error
clase:
{
_com_error error(hresult);
LPCTSTR errorText = error.ErrorMessage();
}
No es parte de MFC o ATL directamente que yo sepa.
RegCreateKeyEx
devuelve aLONG
. Sus documentos dicen que puedo usarFormatMessage
para recuperar el error, pero tengo que convertirloLONG
en un archivoHRESULT
.Tenga en cuenta que no puede hacer lo siguiente:
{ LPCTSTR errorText = _com_error(hresult).ErrorMessage(); // do something with the error... //automatic cleanup when error goes out of scope }
A medida que la clase se crea y se destruye en la pila, se deja errorText para apuntar a una ubicación no válida. En la mayoría de los casos, esta ubicación aún contendrá la cadena de error, pero esa probabilidad desaparece rápidamente al escribir aplicaciones con subprocesos.
Por lo tanto, siempre hágalo de la siguiente manera, como respondió Shog9 arriba:
{ _com_error error(hresult); LPCTSTR errorText = error.ErrorMessage(); // do something with the error... //automatic cleanup when error goes out of scope }
fuente
_com_error
objeto se crea en la pila en ambos ejemplos. El término que estás buscando es temporal . En el ejemplo anterior, el objeto es un temporal que se destruye al final de la declaración.std::wstring strErrorText = _com_error(hresult).ErrorMessage();
Prueba esto:
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); }
fuente
Esto es más una adición a la mayoría de las respuestas, pero en lugar de usar
LocalFree(errorText)
use laHeapFree
función:::HeapFree(::GetProcessHeap(), NULL, errorText);
Desde el sitio de MSDN :
Actualización
que encontré
LocalFree
en la versión 10.0.10240.0 del SDK (línea 1108 en WinBase.h). Sin embargo, la advertencia todavía existe en el enlace anterior.#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
Actualización 2
También sugeriría usar la
FORMAT_MESSAGE_MAX_WIDTH_MASK
bandera para arreglar los saltos de línea en los mensajes del sistema.Desde el sitio de MSDN :
Actualización 3
Parece haber 2 códigos de error del sistema en particular que no devuelven el mensaje completo utilizando el enfoque recomendado:
¿Por qué FormatMessage solo crea mensajes parciales para los errores del sistema ERROR_SYSTEM_PROCESS_TERMINATED y ERROR_UNHANDLED_EXCEPTION?
fuente
Aquí hay una versión de la función de David que maneja 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);
}
fuente
_sntprintf_s
en el caso de UNICODE. La función toma el número de caracteres, por lo que desea_countof
oARRAYSIZE
también conocido como ensizeof(buffer) / sizeof(buffer[0])
lugar desizeof
.Desde c ++ 11, puede usar la biblioteca estándar en lugar de
FormatMessage
:#include <system_error> std::string message = std::system_category().message(hr)
fuente
Como se señaló en otras respuestas:
FormatMessage
toma unDWORD
resultado no unHRESULT
(típicamenteGetLastError()
).LocalFree
es necesario para liberar la memoria asignada porFormatMessage
Tomé los puntos anteriores y agregué algunos más para mi respuesta:
FormatMessage
en una clase para asignar y liberar memoria según sea necesariooperator LPTSTR() const { return ...; }
para que su clase se pueda usar como una cadenaclass 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; };
Encuentre una versión más completa del código anterior aquí: https://github.com/stephenquan/FormatMessage
Con la clase anterior, el uso es simplemente:
std::wcout << (LPTSTR) CFormatMessage(GetLastError()) << L"\n";
fuente
El siguiente código es el equivalente en C ++ que escribí en contraste con ErrorSalir () de Microsoft, pero ligeramente alterado para evitar todas las macros y usar Unicode. La idea aquí es evitar moldes y mallocs innecesarios. No pude escapar de todos los elencos de C, pero esto es lo mejor que pude reunir. Perteneciente a FormatMessageW (), que requiere que la función de formato asigne un puntero y el ID de error de GetLastError (). El puntero después de static_cast se puede utilizar como un puntero wchar_t normal.
#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); }
fuente