para - setlocale c++
¿Cómo debo usar FormatMessage() correctamente en C++? (6)
Sin :
- MFC
- ATL
¿Cómo puedo usar FormatMessage()
para obtener el texto de error para 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);
}
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);
}
El código a continuación es código, es el equivalente de C ++ que he escrito, en contraste con ErrorExit () de Microsoft, pero ligeramente modificado para evitar todas las macros y usar unicode. La idea aquí es evitar moldes y mallocs innecesarios. No pude escapar de todos los moldes C, pero esto es lo mejor que pude reunir. Perteneciente a FormatMessageW (), que requiere un puntero para ser asignado por la función de formato y el Error Id de GetLastError (). El puntero después de static_cast se puede usar 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);
}
Esta es la forma correcta de recuperar un mensaje de error del sistema para un HRESULT
(llamado hresult en este caso, o puede reemplazarlo por 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;
}
La diferencia clave entre esto y la respuesta de David Hanak es el uso de la bandera FORMAT_MESSAGE_IGNORE_INSERTS
. MSDN no está seguro de 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ás usando Visual C ++ puedes hacer tu vida un poco más fácil usando la clase _com_error
:
{
_com_error error(hresult);
LPCTSTR errorText = error.ErrorMessage();
// do something with the error...
//automatic cleanup when error goes out of scope
}
No es parte de MFC o ATL directamente, que yo sepa.
Esto es más una adición a la mayoría de las respuestas, pero en lugar de usar LocalFree(errorText)
usa la función HeapFree
:
::HeapFree(::GetProcessHeap(), NULL, errorText);
Windows 10 :
LocalFree no está en el SDK moderno, por lo que no se puede usar para liberar el búfer de resultados. En su lugar, use HeapFree (GetProcessHeap (), assignedMessage). En este caso, esto es lo mismo que llamar a LocalFree en la memoria.
Actualizar
Descubrí que LocalFree
está 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 de arriba.
#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 sugiero usar el indicador FORMAT_MESSAGE_MAX_WIDTH_MASK
para ordenar los saltos de línea en los mensajes del sistema.
FORMAT_MESSAGE_MAX_WIDTH_MASK
La función ignora los saltos de línea regulares en el texto de definición del mensaje. La función almacena saltos de línea codificados en el texto de definición de mensaje en el búfer de salida. La función no genera nuevos saltos de línea.
Actualización 3
Parece que hay dos códigos de error de sistema particulares que no devuelven el mensaje completo utilizando el enfoque recomendado:
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);
}
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 que apunte 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 se desvanece rápidamente al escribir aplicaciones con subprocesos.
Por lo tanto, siempre hazlo de la siguiente manera, respondiendo por Shog9 arriba:
{
_com_error error(hresult);
LPCTSTR errorText = error.ErrorMessage();
// do something with the error...
//automatic cleanup when error goes out of scope
}