solía haber una búsqueda de error exe en la sección de herramientas en visual studio que lo hace bastante bien cuando solo necesita un mensaje de error para la depuración.
ColdCat
@ColdCat: para la depuración es mucho más fácil agregar un @err,hrreloj y hacer que el depurador convierta automáticamente el último código de error en una representación legible para humanos. El ,hrespecificador de formato funciona para cualquier expresión que se evalúe como un valor integral, por ejemplo, un 5,hrreloj mostrará "ERROR_ACCESS_DENIED: acceso denegado". .
//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
std::stringGetLastErrorAsString(){//Get the error message, if any.
DWORD errorMessageID =::GetLastError();if(errorMessageID ==0)return std::string();//No error message has been recorded
LPSTR messageBuffer =nullptr;size_t size =FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPSTR)&messageBuffer,0, NULL);
std::string message(messageBuffer, size);//Free the buffer.LocalFree(messageBuffer);return message;}
Creo que realmente necesita pasar (LPSTR)&messageBufferen este caso, ya que de lo contrario no hay forma de que FormatMessageA pueda alterar su valor para apuntar al búfer asignado.
Kylotan
2
Oh, wow, sí, eso es un poco raro. ¿Cómo modificaría el puntero? Pero pasándole la dirección del puntero (puntero a puntero), pero lanzándolo a un puntero normal ... Win32 rareza. Gracias por el aviso, lo arreglé en mi propio código base (y mi respuesta). Captura muy sutil.
Jamin Gray
1
Muchas gracias, su ejemplo es mucho más claro que el de MSDN. Más aún, el de MSDN ni siquiera pudo compilar. Incluye algún strsafe.hencabezado, que no es seguro en absoluto, ya que causa un montón de errores de compilación en winuser.hy winbase.h.
Hi-Angel
2
Algunas ID de error no son compatibles. Por ejemplo, 0x2EE7, ERROR_INTERNET_NAME_NOT_RESOLVED causa un nuevo error al llamar a FormatMessage: 0x13D, el sistema no puede encontrar el texto del mensaje para el número de mensaje 0x% 1 en el archivo de mensaje para% 2.
Brent
3
Problemas con esta implementación: 1GetLastErrorpotencialmente se llama demasiado tarde. 2No hay soporte Unicode. 3Uso de excepciones sin implementar garantías de seguridad excepcionales.
Inspeccionable el
65
Actualizado (11/2017) para tener en cuenta algunos comentarios.
@ Hi-Angel: el ejemplo supone que está compilando con UNICODE definido. 'FormatMessage' es en realidad una macro que se expande a 'FormatMessageA' para las memorias intermedias de caracteres Ansi / MBCS, o 'FormatMessageW' para las memorias intermedias UTF16 / UNICODE, dependiendo de cómo se compila la aplicación. Me tomé la libertad de editar el ejemplo anterior para invocar explícitamente la versión que coincide con el tipo de búfer de salida (wchar_t).
Problemas con esta implementación: 1Error al especificar el FORMAT_MESSAGE_IGNORE_INSERTSindicador importante . 2GetLastErrorpotencialmente llamado demasiado tarde. 3Restricción arbitraria del mensaje a 256 unidades de código.4Sin manejo de errores.
Inspeccionable el
1
sizeof (buf) debe ser ARRAYSIZE (buf) ya que FormatMessage espera el tamaño del búfer en TCHAR, no en bytes.
Kai
1
¿No podemos usar en 256lugar de (sizeof(buf) / sizeof(wchar_t)? ¿Es aceptable y seguro?
GetLastError devuelve un código de error numérico. Para obtener un mensaje de error descriptivo (por ejemplo, para mostrar a un usuario), puede llamar a FormatMessage :
// This functions fills a caller-defined character buffer (pBuffer)// of max length (cchBufferLength) with the human-readable error message// for a Win32 error code (dwErrorCode).// // Returns TRUE if successful, or FALSE otherwise.// If successful, pBuffer is guaranteed to be NUL-terminated.// On failure, the contents of pBuffer are undefined.
BOOL GetErrorMessage(DWORD dwErrorCode, LPTSTR pBuffer, DWORD cchBufferLength){if(cchBufferLength ==0){return FALSE;}
DWORD cchMsg =FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,/* (not used with FORMAT_MESSAGE_FROM_SYSTEM) */
dwErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
pBuffer,
cchBufferLength,
NULL);return(cchMsg >0);}
En C ++, puede simplificar la interfaz considerablemente utilizando la clase std :: string:
#include<Windows.h>#include<system_error>#include<memory>#include<string>typedef std::basic_string<TCHAR>String;StringGetErrorMessage(DWORD dwErrorCode){
LPTSTR psz{nullptr};const DWORD cchMsg =FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,// (not used with FORMAT_MESSAGE_FROM_SYSTEM)
dwErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),reinterpret_cast<LPTSTR>(&psz),0,
NULL);if(cchMsg >0){// Assign buffer to smart pointer with custom deleter so that memory gets released// in case String's c'tor throws an exception.auto deleter =[](void* p){::LocalFree(p);};
std::unique_ptr<TCHAR,decltype(deleter)> ptrBuffer(psz, deleter);returnString(ptrBuffer.get(), cchMsg);}else{auto error_code{::GetLastError()};throw std::system_error( error_code, std::system_category(),"Failed to retrieve error message string.");}}
NOTA: Estas funciones también funcionan para valores HRESULT. Simplemente cambie el primer parámetro de DWORD dwErrorCode a HRESULT hResult. El resto del código puede permanecer sin cambios.
Estas implementaciones proporcionan las siguientes mejoras sobre las respuestas existentes:
Código de muestra completo, no solo una referencia a la API para llamar.
Proporciona implementaciones C y C ++.
Funciona para la configuración de proyectos Unicode y MBCS.
Toma el código de error como parámetro de entrada. Esto es importante, ya que el último código de error de un hilo solo es válido en puntos bien definidos. Un parámetro de entrada permite que la persona que llama siga el contrato documentado.
Implementa la excepción de seguridad adecuada. A diferencia de todas las otras soluciones que implícitamente usan excepciones, esta implementación no perderá memoria en caso de que se genere una excepción al construir el valor de retorno.
Manejo adecuado de errores / informe de errores, a diferencia de algunas de las otras respuestas, que silenciosamente ignoran los errores.
Esta respuesta ha sido incorporada de Stack Overflow Documentation. Los siguientes usuarios han contribuido al ejemplo: stackptr , Ajay , Cody Gray ♦ , IInspectable .
En lugar de lanzar std::runtime_error, sugiero lanzar std::system_error(lastError, std::system_category(), "Failed to retrieve error message string.")dónde lastErrorestaría el valor de retorno GetLastError()después de la FormatMessage()llamada fallida .
zett42
¿Cuál es la ventaja de usar su versión C FormatMessagedirectamente?
Micha Wiedenmann
@mic: Eso es como preguntar: "¿Cuál es la ventaja de las funciones en C?" Las funciones reducen la complejidad al proporcionar abstracciones. En este caso, la abstracción reduce el número de parámetros de 7 a 3, implementa el contrato documentado de la API al pasar indicadores y parámetros compatibles y codifica la lógica documentada de informe de errores. Proporciona una interfaz concisa, con una semántica fácil de adivinar, ahorrándole la necesidad de invertir 11 minutos para leer la documentación .
Inspeccionable el
Me gusta esta respuesta, está muy claro que se ha prestado especial atención a la corrección del código. Tengo dos preguntas. 1) ¿Por qué lo usa ::HeapFree(::GetProcessHeap(), 0, p)en el eliminador en lugar de ::LocalFree(p)lo que sugiere la documentación? 2) ¿Me doy cuenta de que la documentación dice hacerlo de esta manera, pero no reinterpret_cast<LPTSTR>(&psz)viola la estricta regla de alias?
Teh JoE
@teh: Gracias por los comentarios. Pregunta 1): Honestamente, no sé si esto es seguro. No recuerdo a quién o por qué HeapFreese introdujo la llamada , mientras que esto todavía era un tema en SO Documentation. Tendré que investigar (aunque la documentación parece ser clara, que esto no es seguro). Pregunta 2): Esto es doble. Para una configuración MBCS, LPTSTRes un alias para char*. Ese elenco siempre está a salvo. Para una configuración Unicode, la conversión a wchar_t*es sospechosa en cualquier caso. No sé si esto es seguro en C, pero lo más probable es que no lo sea en C ++. Yo también tendría que investigar esto.
Inspeccionable el
13
En general, debe usar FormatMessagepara convertir un código de error de Win32 a texto.
Formatea una cadena de mensaje. La función requiere una definición de mensaje como entrada. La definición del mensaje puede provenir de un búfer pasado a la función. Puede provenir de un recurso de tabla de mensajes en un módulo ya cargado. O la persona que llama puede pedirle a la función que busque los recursos de la tabla de mensajes del sistema para la definición del mensaje. La función encuentra la definición del mensaje en un recurso de tabla de mensajes basado en un identificador de mensaje y un identificador de idioma. La función copia el texto del mensaje formateado en un búfer de salida, procesando cualquier secuencia de inserción incrustada si se solicita.
Confirmé que este código funciona. ¿No debería TS aceptar esta respuesta?
swdev
2
Si es necesario para un lanzamiento adicional, hay una manera más simple de hacerlo en C # con Win32Exception
SerG
2
@swdev: ¿Por qué alguien debería aceptar una respuesta en C # a una pregunta etiquetada c o c ++ ? Tal como está, esta respuesta ni siquiera aborda la pregunta que se hace.
Inspeccionable el
1
Bueno, no recuerdo haber votado sobre esta respuesta propuesta. Abordé la falla obvia en la lógica de @ swdev. Pero como no me vas a creer, ahora te lo voy a demostrar: Aquí, haz otro voto negativo. Esa es de mi parte, porque esta respuesta, aunque puede ser útil dada una pregunta diferente, simplemente no es útil dada la pregunta que se hizo.
Inspeccionable el
2
"Supongo que tiene ideas útiles para ofrecer más allá de lo obvio" - De hecho, tengo .
Inspeccionable el
4
Si necesita admitir MBCS y Unicode, la respuesta de Mr.C64 no es suficiente. El búfer debe declararse TCHAR y emitirse a LPTSTR. Tenga en cuenta que este código no trata con la molesta nueva línea que Microsoft agrega al mensaje de error.
En caso de que el CStringc'tor arroje una excepción, esta implementación pierde la memoria asignada por la llamada a FormatMessage.
Inspeccionable el
Es cierto, pero he usado este código durante muchos años y nunca ha sido un problema. El único caso en el que es probable que se produzca el CString ctor es la falla en la asignación de memoria, y la mayoría del código MFC, incluidas las cosas proporcionadas por Microsoft, no maneja las condiciones de falta de memoria tan bien como desee. Afortunadamente, la mayoría de las PC ahora tienen tanta memoria que tienes que trabajar bastante duro para usarlo todo. Cualquier uso que genere una instancia CString temporal (incluida la devolución de un CString) corre este riesgo. Es una compensación de riesgo / conveniencia. Además, si sucede en un controlador de mensajes, se detectará la excepción.
victimofleisure
La mayor parte de este comentario está mal, lo siento. "Nunca me pasó" es un punto débil, especialmente cuando sabes cómo puede fallar el código. La cantidad de memoria tampoco tiene impacto en el espacio de direcciones disponible asignado a un proceso. RAM es solo una optimización de rendimiento. Copy-elision evita la asignación de una temporal, cuando el código se escribe para permitir NRVO. El hecho de que las excepciones se encuentren o no en un controlador de mensajes depende del bitness del proceso y la configuración externa. He enviado una respuesta que muestra que la gestión de riesgos no genera inconvenientes.
Inspeccionable el
Además, el riesgo de quedarse sin memoria al construir un temporal es discutible. En ese punto, todos los recursos han sido liberados, y nada malo saldrá de ello.
Inspeccionable el
1
No, lo siento, el código no es útil para ti. Pero TBH es bastante bajo en mi lista de problemas.
victimofleisure
3
voidWinErrorCodeToString(DWORD ErrorCode,string&Message){char* locbuffer = NULL;
DWORD count =FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL,ErrorCode,0,(LPSTR)&locbuffer,0,nullptr);if(locbuffer){if(count){int c;int back =0;//// strip any trailing "\r\n"s and replace by a single "\n"//while(((c =*CharPrevA(locbuffer, locbuffer + count))=='\r')||(c =='\n')){
count--;
back++;}if(back){
locbuffer[count++]='\n';
locbuffer[count]='\0';}Message="Error: ";Message+= locbuffer;}LocalFree(locbuffer);}else{Message="Unknown error code: "+ to_string(ErrorCode);}}
Problemas con esta implementación: 1No hay soporte Unicode. 2Formateo inapropiado del mensaje de error. Si la persona que llama necesita procesar la cadena devuelta, simplemente puede hacerlo. Su implementación deja a la persona que llama sin opción. 3Uso de excepciones pero falta de seguridad de excepción adecuada. En caso de que los std::stringoperadores arrojen excepciones, el búfer asignado por FormatMessagese filtra. 4¿Por qué no simplemente devolver un en std::stringlugar de que la persona que llama pase un objeto por referencia?
Inspeccionable el
1
Desde c ++ 11, puede usar la biblioteca estándar en lugar de FormatMessage:
#include<system_error>
std::string GetLastErrorAsString(){
DWORD errorMessageID =::GetLastError();if(errorMessageID ==0){return std::string();//No error message has been recorded}else{return std::system_category().message(errorMessageID);}}
Solo hay una pequeña ventana donde las llamadas GetLastErrorproducen un resultado significativo. Siendo esto C ++, la única opción segura aquí es que la persona que llama proporcione el último código de error. Ciertamente no ayuda, que el código presentó llamadas GetLastErrordos veces . Además, aunque es conveniente, la falta inherente de C ++ de compatibilidad con caracteres anchos no hace que la error_categoryinterfaz sea universalmente útil. Esto solo se suma al largo historial de oportunidades perdidas de C ++.
Insensible
Buen punto sobre la inútil llamada a GetLastError. Pero no veo diferencia entre llamar GetLastErroraquí o que la persona que llama lo llame. Con respecto a C ++ y wchar: no abandone la esperanza, Microsoft está comenzando a permitir que las aplicaciones sean solo UTF-8 .
Crónica
"No veo ninguna diferencia" - Considere el siguiente sitio llamada: log_error("error", GetLastErrorAsString());. Considere también que log_errorel primer argumento es de tipo std::string. La llamada de conversión (invisible) c'tor acaba de abandonar sus garantías para capturar un valor significativo desde GetLastErrorel punto en el que lo está llamando.
Insensible
¿Por qué crees que el constructor std :: string llama a una función Win32?
Crónica
1
mallocpide HeapAllocel montón de procesos. El montón de procesos es cultivable. Si se necesita para crecer, que en última instancia llamar VirtualAlloc , que no establece último código de error del subproceso de llamada. Ahora, eso es totalmente perder el punto, que es: C ++ es un campo minado. Esta implementación solo se suma a eso, al proporcionar una interfaz con garantías implícitas que simplemente no puede cumplir. Si cree que no hay ningún problema, debería ser fácil para usted probar la corrección de la solución propuesta. Buena suerte.
Insensible
0
Dejaré esto aquí ya que tendré que usarlo más tarde. Es una fuente para una pequeña herramienta compatible binaria que funcionará igualmente bien en ensamblaje, C y C ++.
GetErrorMessageLib.c (compilado a GetErrorMessageLib.dll)
#include<Windows.h>/***
* returns 0 if there was enough space, size of buffer in bytes needed
* to fit the result, if there wasn't enough space. -1 on error.
*/
__declspec(dllexport)intGetErrorMessageA(DWORD dwErrorCode, LPSTR lpResult, DWORD dwBytes){
LPSTR tmp;
DWORD result_len;
result_len =FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,
dwErrorCode,
LANG_SYSTEM_DEFAULT,(LPSTR)&tmp,0,
NULL
);if(result_len ==0){return-1;}// FormatMessage's return is 1 character too short.++result_len;
strncpy(lpResult, tmp, dwBytes);
lpResult[dwBytes -1]=0;LocalFree((HLOCAL)tmp);if(result_len <= dwBytes){return0;}else{return result_len;}}/***
* returns 0 if there was enough space, size of buffer in bytes needed
* to fit the result, if there wasn't enough space. -1 on error.
*/
__declspec(dllexport)intGetErrorMessageW(DWORD dwErrorCode, LPWSTR lpResult, DWORD dwBytes){
LPWSTR tmp;
DWORD nchars;
DWORD result_bytes;
nchars = dwBytes >>1;
result_bytes =2*FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,
dwErrorCode,
LANG_SYSTEM_DEFAULT,(LPWSTR)&tmp,0,
NULL
);if(result_bytes ==0){return-1;}// FormatMessage's return is 1 character too short.
result_bytes +=2;
wcsncpy(lpResult, tmp, nchars);
lpResult[nchars -1]=0;LocalFree((HLOCAL)tmp);if(result_bytes <= dwBytes){return0;}else{return result_bytes *2;}}
versión en línea (GetErrorMessage.h):
#ifndefGetErrorMessage_H#defineGetErrorMessage_H#include<Windows.h>/***
* returns 0 if there was enough space, size of buffer in bytes needed
* to fit the result, if there wasn't enough space. -1 on error.
*/staticinlineintGetErrorMessageA(DWORD dwErrorCode, LPSTR lpResult, DWORD dwBytes){
LPSTR tmp;
DWORD result_len;
result_len =FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,
dwErrorCode,
LANG_SYSTEM_DEFAULT,(LPSTR)&tmp,0,
NULL
);if(result_len ==0){return-1;}// FormatMessage's return is 1 character too short.++result_len;
strncpy(lpResult, tmp, dwBytes);
lpResult[dwBytes -1]=0;LocalFree((HLOCAL)tmp);if(result_len <= dwBytes){return0;}else{return result_len;}}/***
* returns 0 if there was enough space, size of buffer in bytes needed
* to fit the result, if there wasn't enough space. -1 on error.
*/staticinlineintGetErrorMessageW(DWORD dwErrorCode, LPWSTR lpResult, DWORD dwBytes){
LPWSTR tmp;
DWORD nchars;
DWORD result_bytes;
nchars = dwBytes >>1;
result_bytes =2*FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,
dwErrorCode,
LANG_SYSTEM_DEFAULT,(LPWSTR)&tmp,0,
NULL
);if(result_bytes ==0){return-1;}// FormatMessage's return is 1 character too short.
result_bytes +=2;
wcsncpy(lpResult, tmp, nchars);
lpResult[nchars -1]=0;LocalFree((HLOCAL)tmp);if(result_bytes <= dwBytes){return0;}else{return result_bytes *2;}}#endif/* GetErrorMessage_H */
caso de uso dinámico (se supone que el código de error es válido; de lo contrario, se necesita una comprobación de -1):
ejemplo que se usa con el ensamblaje gnu como en MinGW32 (de nuevo, se supone que el código de error es válido, de lo contrario se necesita -1 verificación).
Esto realmente no agrega nada útil. Además de eso, llama a la versión ANSI de FormatMessage, sin razón aparente, y se limita arbitrariamente a 80 caracteres, de nuevo, sin razón alguna. Me temo que esto no es útil.
Inspectable el
Tienes razón, esperaba que nadie se diera cuenta de la falta de la versión Unicode. Veré cómo definir una cadena Unicode en gnu as y modificaré mi solución. Perdón por la deshonestidad.
Dmitry
ok la versión unicode está activada. y no es una razón arbitraria; Todos los mensajes de error tienen menos de 80 caracteres o no vale la pena leerlos, y el código de error es más importante que el mensaje de error. No hay mensajes de error estándar que superen los 80 caracteres, por lo que es una suposición segura, y cuando no lo es, no pierde memoria.
Dmitry
3
"todos los mensajes de error tienen menos de 80 caracteres o no vale la pena leerlos" , eso está mal. El mensaje de error para ERROR_LOCK_VIOLATION(33) es: "El proceso no puede acceder al archivo porque otro proceso ha bloqueado una parte del archivo". Eso es claramente más de 80 caracteres, y vale la pena leerlo, si está tratando de resolver un problema y encontrarlo en un archivo de registro de diagnóstico. Esta respuesta no agrega ningún valor sustancial sobre las respuestas existentes.
Inspectable el
Esto no es menos ingenuo. Simplemente falla con menos frecuencia. Excepto por la implementación del ensamblaje que se basa en el tamaño del búfer (que dice tener espacio para 200 caracteres, cuando solo tiene espacio para 100). Nuevamente, esto no agrega nada sustancial, eso no está en ninguna de las otras respuestas. En particular, es peor que esta respuesta . Vea la lista de viñetas allí y observe qué problemas tiene su implementación propuesta, además de los que acabo de señalar.
@err,hr
reloj y hacer que el depurador convierta automáticamente el último código de error en una representación legible para humanos. El,hr
especificador de formato funciona para cualquier expresión que se evalúe como un valor integral, por ejemplo, un5,hr
reloj mostrará "ERROR_ACCESS_DENIED: acceso denegado". .GetLastError()
documentación: " Para obtener una cadena de error para los códigos de error del sistema, use laFormatMessage()
función ". Vea el ejemplo Recuperando el código del último error en MSDN.Respuestas:
fuente
(LPSTR)&messageBuffer
en este caso, ya que de lo contrario no hay forma de que FormatMessageA pueda alterar su valor para apuntar al búfer asignado.strsafe.h
encabezado, que no es seguro en absoluto, ya que causa un montón de errores de compilación enwinuser.h
ywinbase.h
.1
GetLastError
potencialmente se llama demasiado tarde.2
No hay soporte Unicode.3
Uso de excepciones sin implementar garantías de seguridad excepcionales.Actualizado (11/2017) para tener en cuenta algunos comentarios.
Ejemplo fácil:
fuente
1
Error al especificar elFORMAT_MESSAGE_IGNORE_INSERTS
indicador importante .2
GetLastError
potencialmente llamado demasiado tarde.3
Restricción arbitraria del mensaje a 256 unidades de código.4
Sin manejo de errores.256
lugar de(sizeof(buf) / sizeof(wchar_t)
? ¿Es aceptable y seguro?MSDN tiene un código de muestra que muestra cómo usar
FormatMessage()
yGetLastError()
juntos: Recuperar el último código de errorfuente
FormatMessage convertirá el entero de GetLastError en un mensaje de texto.
fuente
GetLastError devuelve un código de error numérico. Para obtener un mensaje de error descriptivo (por ejemplo, para mostrar a un usuario), puede llamar a FormatMessage :
En C ++, puede simplificar la interfaz considerablemente utilizando la clase std :: string:
NOTA: Estas funciones también funcionan para valores HRESULT. Simplemente cambie el primer parámetro de DWORD dwErrorCode a HRESULT hResult. El resto del código puede permanecer sin cambios.
Estas implementaciones proporcionan las siguientes mejoras sobre las respuestas existentes:
FORMAT_MESSAGE_IGNORE_INSERTS
bandera. Ver la importancia de la bandera FORMAT_MESSAGE_IGNORE_INSERTS para obtener más información.Esta respuesta ha sido incorporada de Stack Overflow Documentation. Los siguientes usuarios han contribuido al ejemplo: stackptr , Ajay , Cody Gray ♦ , IInspectable .
fuente
std::runtime_error
, sugiero lanzarstd::system_error(lastError, std::system_category(), "Failed to retrieve error message string.")
dóndelastError
estaría el valor de retornoGetLastError()
después de laFormatMessage()
llamada fallida .FormatMessage
directamente?::HeapFree(::GetProcessHeap(), 0, p)
en el eliminador en lugar de::LocalFree(p)
lo que sugiere la documentación? 2) ¿Me doy cuenta de que la documentación dice hacerlo de esta manera, pero noreinterpret_cast<LPTSTR>(&psz)
viola la estricta regla de alias?HeapFree
se introdujo la llamada , mientras que esto todavía era un tema en SO Documentation. Tendré que investigar (aunque la documentación parece ser clara, que esto no es seguro). Pregunta 2): Esto es doble. Para una configuración MBCS,LPTSTR
es un alias parachar*
. Ese elenco siempre está a salvo. Para una configuración Unicode, la conversión awchar_t*
es sospechosa en cualquier caso. No sé si esto es seguro en C, pero lo más probable es que no lo sea en C ++. Yo también tendría que investigar esto.En general, debe usar
FormatMessage
para convertir un código de error de Win32 a texto.De la documentación de MSDN :
La declaración de FormatMessage:
fuente
Si usa C #, puede usar este código:
fuente
Si necesita admitir MBCS y Unicode, la respuesta de Mr.C64 no es suficiente. El búfer debe declararse TCHAR y emitirse a LPTSTR. Tenga en cuenta que este código no trata con la molesta nueva línea que Microsoft agrega al mensaje de error.
Además, por brevedad, encuentro útil el siguiente método:
fuente
CString
c'tor arroje una excepción, esta implementación pierde la memoria asignada por la llamada aFormatMessage
.fuente
1
No hay soporte Unicode.2
Formateo inapropiado del mensaje de error. Si la persona que llama necesita procesar la cadena devuelta, simplemente puede hacerlo. Su implementación deja a la persona que llama sin opción.3
Uso de excepciones pero falta de seguridad de excepción adecuada. En caso de que losstd::string
operadores arrojen excepciones, el búfer asignado porFormatMessage
se filtra.4
¿Por qué no simplemente devolver un enstd::string
lugar de que la persona que llama pase un objeto por referencia?Desde c ++ 11, puede usar la biblioteca estándar en lugar de
FormatMessage
:fuente
GetLastError
producen un resultado significativo. Siendo esto C ++, la única opción segura aquí es que la persona que llama proporcione el último código de error. Ciertamente no ayuda, que el código presentó llamadasGetLastError
dos veces . Además, aunque es conveniente, la falta inherente de C ++ de compatibilidad con caracteres anchos no hace que laerror_category
interfaz sea universalmente útil. Esto solo se suma al largo historial de oportunidades perdidas de C ++.GetLastError
. Pero no veo diferencia entre llamarGetLastError
aquí o que la persona que llama lo llame. Con respecto a C ++ y wchar: no abandone la esperanza, Microsoft está comenzando a permitir que las aplicaciones sean solo UTF-8 .log_error("error", GetLastErrorAsString());
. Considere también quelog_error
el primer argumento es de tipostd::string
. La llamada de conversión (invisible) c'tor acaba de abandonar sus garantías para capturar un valor significativo desdeGetLastError
el punto en el que lo está llamando.malloc
pideHeapAlloc
el montón de procesos. El montón de procesos es cultivable. Si se necesita para crecer, que en última instancia llamar VirtualAlloc , que no establece último código de error del subproceso de llamada. Ahora, eso es totalmente perder el punto, que es: C ++ es un campo minado. Esta implementación solo se suma a eso, al proporcionar una interfaz con garantías implícitas que simplemente no puede cumplir. Si cree que no hay ningún problema, debería ser fácil para usted probar la corrección de la solución propuesta. Buena suerte.Dejaré esto aquí ya que tendré que usarlo más tarde. Es una fuente para una pequeña herramienta compatible binaria que funcionará igualmente bien en ensamblaje, C y C ++.
GetErrorMessageLib.c (compilado a GetErrorMessageLib.dll)
versión en línea (GetErrorMessage.h):
caso de uso dinámico (se supone que el código de error es válido; de lo contrario, se necesita una comprobación de -1):
caso de uso regular (se supone que el código de error es válido; de lo contrario, se necesita -1 verificación de retorno):
ejemplo que se usa con el ensamblaje gnu como en MinGW32 (de nuevo, se supone que el código de error es válido, de lo contrario se necesita -1 verificación).
resultado:
The process cannot access the file because another process has locked a portion of the file.
fuente
FormatMessage
, sin razón aparente, y se limita arbitrariamente a 80 caracteres, de nuevo, sin razón alguna. Me temo que esto no es útil.ERROR_LOCK_VIOLATION
(33) es: "El proceso no puede acceder al archivo porque otro proceso ha bloqueado una parte del archivo". Eso es claramente más de 80 caracteres, y vale la pena leerlo, si está tratando de resolver un problema y encontrarlo en un archivo de registro de diagnóstico. Esta respuesta no agrega ningún valor sustancial sobre las respuestas existentes.