costumava haver uma pesquisa de erro exe na seção de ferramentas do visual studio, que faz isso muito bem quando você só precisa de uma mensagem de erro para depuração.
ColdCat 11/01
@ColdCat: Para a depuração, é muito mais fácil adicionar um @err,hrrelógio e fazer com que o depurador converta automaticamente o último código de erro em uma representação legível por humanos. O ,hrespecificador de formato funciona para qualquer expressão que seja avaliada como um valor integral, por exemplo, um 5,hrrelógio exibirá "ERROR_ACCESS_DENIED: acesso negado". .
//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;}
Eu acredito que você realmente precisa passar (LPSTR)&messageBufferneste caso, caso contrário, não há como o FormatMessageA alterar seu valor para apontar para o buffer alocado.
Kylotan
2
Oh, uau, sim, isso é meio estranho. Como isso modificaria o ponteiro? Mas passar o endereço do ponteiro (ponteiro para um ponteiro), mas convertê-lo em um ponteiro regular ... Estranho no Win32. Obrigado pelo aviso, consertei na minha própria base de código (e na minha resposta). Captura muito sutil.
Jamin Grey
1
Muito obrigado, seu exemplo é muito mais claro que o do MSDN. Mais ainda, o do MSDN nem conseguiu compilar. Inclui algum strsafe.hcabeçalho, que não é seguro , causando muitos erros de compilador no winuser.he winbase.h.
Hi-Angel
2
Alguns IDs de erro não são suportados. Por exemplo, 0x2EE7, ERROR_INTERNET_NAME_NOT_RESOLVED causa um novo erro ao chamar FormatMessage: 0x13D. O sistema não pode encontrar o texto da mensagem para o número da mensagem 0x% 1 no arquivo de% 2.
Brent
3
Problemas com esta implementação: 1GetLastErroré potencialmente chamado tarde demais. 2Não há suporte para Unicode. 3Uso de exceções sem implementar garantias de segurança de exceções.
IInspectable
65
Atualizado (11/2017) para levar em consideração alguns comentários.
@ Hi-Angel - O exemplo assume que você está compilando com o UNICODE definido. 'FormatMessage' é na verdade uma macro que se expande para 'FormatMessageA' para buffers de caracteres Ansi / MBCS ou 'FormatMessageW' para buffers UTF16 / UNICODE, dependendo de como o aplicativo é compilado. Tomei a liberdade de editar o exemplo acima para chamar explicitamente a versão que corresponde ao tipo de buffer de saída (wchar_t).
Bukes
2
Adicione FORMAT_MESSAGE_IGNORE_INSERTS: "Se você não está no controle da cadeia de formatação, deve passar FORMAT_MESSAGE_IGNORE_INSERTS para impedir que o% 1 cause problemas." blogs.msdn.microsoft.com/oldnewthing/20071128-00/?p=24353
Roi Danton
1
Problemas com esta implementação: 1Falha ao especificar o FORMAT_MESSAGE_IGNORE_INSERTSsinalizador importante . 2GetLastErrorpotencialmente chamado tarde demais. 3Restrição arbitrária da mensagem para 256 unidades de código. 4Sem manipulação de erros.
IInspectable
1
sizeof (buf) deve ser ARRAYSIZE (buf), pois FormatMessage espera o tamanho do buffer em TCHARs, não em bytes.
Kai
1
Não podemos usar em 256vez de (sizeof(buf) / sizeof(wchar_t)? aceitável e seguro?
GetLastError retorna um código de erro numérico. Para obter uma mensagem de erro descritiva (por exemplo, para exibir para um usuário), você pode chamar 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);}
No C ++, você pode simplificar consideravelmente a interface usando a classe 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: Essas funções também funcionam para valores HRESULT. Apenas mude o primeiro parâmetro de DWORD dwErrorCode para HRESULT hResult. O restante do código pode permanecer inalterado.
Essas implementações fornecem as seguintes melhorias em relação às respostas existentes:
Código de amostra completo, não apenas uma referência à API a ser chamada.
Fornece implementações em C e C ++.
Funciona para configurações de projeto Unicode e MBCS.
Toma o código de erro como um parâmetro de entrada. Isso é importante, pois o último código de erro de um encadeamento é válido apenas em pontos bem definidos. Um parâmetro de entrada permite que o chamador siga o contrato documentado.
Implementa a segurança de exceção adequada. Ao contrário de todas as outras soluções que usam implicitamente exceções, essa implementação não vazará memória no caso de uma exceção ser lançada ao construir o valor de retorno.
Tratamento adequado de erros / relatório de erros, ao contrário de outras respostas, que ignoram silenciosamente os erros.
Esta resposta foi incorporada da Stack Overflow Documentation. Os seguintes usuários contribuíram para o exemplo: stackptr , Ajay , Cody Gray ♦ , IInspectable .
Em vez de jogar std::runtime_error, sugiro jogar para std::system_error(lastError, std::system_category(), "Failed to retrieve error message string.")onde lastErrorseria o valor de retorno GetLastError()após a falha na FormatMessage()chamada.
Zett42 4/17
Qual é a vantagem de usar sua versão C FormatMessagediretamente?
Micha Wiedenmann
@mic: Isso é como perguntar: "Qual é a vantagem das funções em C?" As funções reduzem a complexidade fornecendo abstrações. Nesse caso, a abstração reduz o número de parâmetros de 7 para 3, implementa o contrato documentado da API passando sinalizadores e parâmetros compatíveis e codifica a lógica do relatório de erros documentado. Ele fornece uma interface concisa, com semântica fácil de adivinhar, poupando a necessidade de investir 11 minutos para ler a documentação .
11nspectable
Gosto dessa resposta, é muito claro que foi dada muita atenção à correção do código. Eu tenho duas perguntas. 1) Por que você usa ::HeapFree(::GetProcessHeap(), 0, p)o deleter em vez de ::LocalFree(p)como a documentação sugere? 2) Percebo que a documentação diz para fazer dessa maneira, mas não reinterpret_cast<LPTSTR>(&psz)viola a regra estrita de alias?
TeJ JoE
@teh: Obrigado pelo feedback. Pergunta 1): Honestamente, não sei se isso é seguro. Não me lembro de quem ou por que a chamada HeapFreefoi apresentada, enquanto este ainda era um tópico na documentação do SO. Vou ter que investigar (embora a documentação pareça clara, isso não é seguro). Pergunta 2): Isso é duplo. Para uma configuração do MBCS, LPTSTRé um alias para char*. Esse elenco é sempre seguro. Para uma configuração Unicode, converter para wchar_t*é suspeito de qualquer forma. Não sei se isso é seguro em C, mas provavelmente não em C ++. Eu teria que investigar isso também.
2nspectable em 04/08
13
Em geral, você precisa usar FormatMessagepara converter de um código de erro do Win32 em texto.
Formata uma sequência de mensagens. A função requer uma definição de mensagem como entrada. A definição da mensagem pode vir de um buffer passado para a função. Pode vir de um recurso da tabela de mensagens em um módulo já carregado. Ou o chamador pode solicitar à função que procure os recursos da tabela de mensagens do sistema para a definição da mensagem. A função localiza a definição de mensagem em um recurso da tabela de mensagens com base em um identificador de mensagem e um identificador de idioma. A função copia o texto da mensagem formatada em um buffer de saída, processando quaisquer seqüências de inserção incorporadas, se solicitado.
Confirmei que este código funciona. TS não deveria aceitar esta resposta?
swdev
2
Se for necessário por mais um arremesso há uma maneira mais simples de fazê-lo em C # com Win32Exception
Serg
2
@swdev: Por que alguém deveria aceitar uma resposta em c # para uma pergunta marcada c ou c ++ ? Tal como está, esta resposta nem sequer aborda a pergunta que está sendo feita.
IInspectable
1
Bem, não me lembro de ter votado nesta resposta proposta. Abordei a falha óbvia na lógica do @ swdev. Mas como você não vai acreditar em mim, agora vou provar para você: Aqui, faça outra votação negativa. Essa é minha, porque essa resposta - embora possa ser útil dada uma pergunta diferente - simplesmente não é útil, dada a pergunta que estava sendo feita.
IInspectable
2
"Eu acho que você tem idéias úteis a oferecer além do óbvio" - Na verdade, eu tenho .
IInspectable
4
Se você precisa oferecer suporte ao MBCS e ao Unicode, a resposta do Sr. C64 não é suficiente. O buffer deve ser declarado TCHAR e convertido para LPTSTR. Observe que esse código não lida com a nova linha irritante que a Microsoft anexa à mensagem de erro.
Caso o CStringc'tor gere uma exceção, essa implementação vazará a memória alocada pela chamada para FormatMessage.
IInspectable
É verdade, mas eu uso esse código há muitos anos e nunca foi um problema. O único caso em que o CString é suscetível de ser lançado é a falha na alocação de memória, e a maioria dos códigos MFC - incluindo os itens fornecidos pela Microsoft - não lida com as condições de falta de memória da maneira mais agradável possível. Felizmente, a maioria dos PCs agora tem tanta memória que você precisa trabalhar bastante para usar tudo. Qualquer uso que gera uma instância temporária do CString (incluindo o retorno de um CString) corre esse risco. É uma troca de risco / conveniência. Além disso, se isso ocorrer em um manipulador de mensagens, a exceção será capturada.
victimofleisure
A maior parte deste comentário está errada, desculpe. "Isso nunca aconteceu comigo" é um ponto fraco, especialmente quando você sabe como o código pode falhar. A quantidade de memória também não afeta o espaço de endereço disponível alocado para um processo. RAM é apenas uma otimização de desempenho. A eliminação de cópias evita a alocação temporária, quando o código é gravado para permitir a NRVO. O fato de as exceções serem capturadas ou não em um manipulador de mensagens depende da configuração do processo e das configurações externas. Enviei uma resposta que mostra que o gerenciamento de riscos não causa transtornos.
11nspectable
Além disso, o risco de ficar sem memória na construção de um temporário é discutível. Nesse ponto, todos os recursos foram liberados e nada de ruim resultará disso.
11nspectable
1
Não, desculpe o código não ser útil para você. Mas TBH é muito baixo na minha 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 com esta implementação: 1Não há suporte para Unicode. 2Formatação inadequada da mensagem de erro. Se o chamador precisar processar a sequência retornada, poderá fazê-lo. Sua implementação deixa o chamador sem opção. 3Uso de exceções, mas falta de segurança adequada para exceções. Caso os std::stringoperadores lançem exceções, o buffer alocado por FormatMessageé vazado. 4Por que não simplesmente retornar a em std::stringvez de fazer com que o chamador passe um objeto por referência?
IInspectable
1
Desde o c ++ 11, você pode usar a biblioteca padrão em vez 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);}}
Há apenas uma pequena janela em que a chamada GetLastErrorproduz um resultado significativo. Sendo esse C ++, a única opção segura aqui é fazer com que o chamador forneça o último código de erro. Certamente não ajuda, que o código apresentado chama GetLastErrorduas vezes . Além disso, embora conveniente, a falta inerente de amplo suporte a caracteres do C ++ falha ao tornar a error_categoryinterface universalmente útil. Isso apenas contribui para o longo histórico de oportunidades perdidas do C ++.
IInspectable
Bom ponto sobre a chamada inútil para GetLastError. Mas não vejo diferença entre ligar GetLastErroraqui ou fazer com que o chamador ligue. Em relação ao C ++ e ao wchar: não abandone a esperança, a Microsoft está começando a permitir que os aplicativos sejam apenas UTF-8 .
Chronial
"Eu não vejo nenhuma diferença" - Considere o seguinte site de chamada: log_error("error", GetLastErrorAsString());. Considere também que log_erroro primeiro argumento desse tipo é do tipo std::string. A chamada de invisibilidade (conversão invisível) acabou de abandonar suas garantias para capturar um valor significativo GetLastErrorno momento em que você está chamando.
IInspectable
Por que você acha que o construtor std :: string chama uma função Win32?
Chronial
1
mallocsolicita HeapAlloco heap do processo. O heap do processo é expansível. Se ele precisa para crescer, ele acabará por chamar VirtualAlloc , que não definida último código de erro do segmento de chamada. Agora isso está totalmente errado, o que é: C ++ é um campo minado. Essa implementação apenas contribui para isso, fornecendo uma interface com garantias implícitas que ela simplesmente não pode cumprir. Se você acredita que não há problema, deve ser fácil provar a correção da solução proposta. Boa sorte.
IInspectable
0
vou deixar isso aqui, pois precisarei usá-lo mais tarde. É uma fonte para uma pequena ferramenta compatível com binários que funcionará igualmente bem em montagem, C e C ++.
GetErrorMessageLib.c (compilado em 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;}}
versão embutida (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 (assumindo que o código de erro é válido, caso contrário, é necessária uma verificação -1):
exemplo usando with assembly gnu como no MinGW32 (novamente, assumindo que o código de erro é válido, caso contrário, é necessária uma verificação -1).
Isso realmente não adiciona nada de útil. Além disso, ele chama a versão ANSI de FormatMessage, sem motivo aparente, e se limita arbitrariamente a 80 caracteres, novamente, por nenhum motivo. Receio, isso não ajuda.
IInspectable
você está certo, eu esperava que ninguém notasse a falta da versão Unicode. Vou verificar como definir uma string Unicode no gnu e modificar minha solução. desculpe pela desonestidade.
Dmitry
ok versão unicode está pronta. e não é uma razão arbitrária; todas as mensagens de erro têm menos de 80 caracteres ou não valem a pena ler, e o código de erro é mais importante que a mensagem de erro. Não há mensagens de erro padrão que excedam 80 caracteres, portanto é uma suposição segura e, quando não é, não perde memória.
Dmitry
3
"todas as mensagens de erro têm menos de 80 caracteres ou não valem a pena ler" - Isso está errado. A mensagem de erro para ERROR_LOCK_VIOLATION(33) é: "O processo não pode acessar o arquivo porque outro processo bloqueou uma parte do arquivo". São ambos claramente maiores que 80 caracteres e vale muito a pena ler, se você estiver tentando resolver um problema e encontrar isso em um arquivo de log de diagnóstico. Esta resposta não agrega nenhum valor substancial sobre as respostas existentes.
IInspectable
Isso não é menos ingênuo. Apenas falha com menos frequência. Exceto pela implementação da montagem que se refere ao tamanho do buffer (alegando ter espaço para 200 caracteres, quando só tem espaço para 100). Novamente, isso não adiciona nada de substancial, isso ainda não está em nenhuma das outras respostas. Em particular, é pior do que esta resposta . Veja a lista de marcadores e observe quais problemas sua implementação proposta possui, além dos que acabei de apontar.
@err,hr
relógio e fazer com que o depurador converta automaticamente o último código de erro em uma representação legível por humanos. O,hr
especificador de formato funciona para qualquer expressão que seja avaliada como um valor integral, por exemplo, um5,hr
relógio exibirá "ERROR_ACCESS_DENIED: acesso negado". .GetLastError()
documentação: " Para obter uma sequência de erros para códigos de erro do sistema, use aFormatMessage()
função. ". Consulte o exemplo Recuperando o código do último erro no MSDN.Respostas:
fonte
(LPSTR)&messageBuffer
neste caso, caso contrário, não há como o FormatMessageA alterar seu valor para apontar para o buffer alocado.strsafe.h
cabeçalho, que não é seguro , causando muitos erros de compilador nowinuser.h
ewinbase.h
.1
GetLastError
é potencialmente chamado tarde demais.2
Não há suporte para Unicode.3
Uso de exceções sem implementar garantias de segurança de exceções.Atualizado (11/2017) para levar em consideração alguns comentários.
Exemplo fácil:
fonte
1
Falha ao especificar oFORMAT_MESSAGE_IGNORE_INSERTS
sinalizador importante .2
GetLastError
potencialmente chamado tarde demais.3
Restrição arbitrária da mensagem para 256 unidades de código.4
Sem manipulação de erros.256
vez de(sizeof(buf) / sizeof(wchar_t)
? aceitável e seguro?O MSDN possui alguns códigos de exemplo que demonstram como usar
FormatMessage()
eGetLastError()
juntos: Recuperando o código do último errofonte
FormatMessage irá transformar o retorno inteiro de GetLastError em uma mensagem de texto.
fonte
GetLastError retorna um código de erro numérico. Para obter uma mensagem de erro descritiva (por exemplo, para exibir para um usuário), você pode chamar FormatMessage :
No C ++, você pode simplificar consideravelmente a interface usando a classe std :: string:
NOTA: Essas funções também funcionam para valores HRESULT. Apenas mude o primeiro parâmetro de DWORD dwErrorCode para HRESULT hResult. O restante do código pode permanecer inalterado.
Essas implementações fornecem as seguintes melhorias em relação às respostas existentes:
FORMAT_MESSAGE_IGNORE_INSERTS
bandeira. Consulte A importância do sinalizador FORMAT_MESSAGE_IGNORE_INSERTS para obter mais informações.Esta resposta foi incorporada da Stack Overflow Documentation. Os seguintes usuários contribuíram para o exemplo: stackptr , Ajay , Cody Gray ♦ , IInspectable .
fonte
std::runtime_error
, sugiro jogar parastd::system_error(lastError, std::system_category(), "Failed to retrieve error message string.")
ondelastError
seria o valor de retornoGetLastError()
após a falha naFormatMessage()
chamada.FormatMessage
diretamente?::HeapFree(::GetProcessHeap(), 0, p)
o deleter em vez de::LocalFree(p)
como a documentação sugere? 2) Percebo que a documentação diz para fazer dessa maneira, mas nãoreinterpret_cast<LPTSTR>(&psz)
viola a regra estrita de alias?HeapFree
foi apresentada, enquanto este ainda era um tópico na documentação do SO. Vou ter que investigar (embora a documentação pareça clara, isso não é seguro). Pergunta 2): Isso é duplo. Para uma configuração do MBCS,LPTSTR
é um alias parachar*
. Esse elenco é sempre seguro. Para uma configuração Unicode, converter parawchar_t*
é suspeito de qualquer forma. Não sei se isso é seguro em C, mas provavelmente não em C ++. Eu teria que investigar isso também.Em geral, você precisa usar
FormatMessage
para converter de um código de erro do Win32 em texto.Na documentação do MSDN :
A declaração de FormatMessage:
fonte
Se você estiver usando c #, poderá usar este código:
fonte
Se você precisa oferecer suporte ao MBCS e ao Unicode, a resposta do Sr. C64 não é suficiente. O buffer deve ser declarado TCHAR e convertido para LPTSTR. Observe que esse código não lida com a nova linha irritante que a Microsoft anexa à mensagem de erro.
Além disso, por uma questão de brevidade, acho útil o seguinte método:
fonte
CString
c'tor gere uma exceção, essa implementação vazará a memória alocada pela chamada paraFormatMessage
.fonte
1
Não há suporte para Unicode.2
Formatação inadequada da mensagem de erro. Se o chamador precisar processar a sequência retornada, poderá fazê-lo. Sua implementação deixa o chamador sem opção.3
Uso de exceções, mas falta de segurança adequada para exceções. Caso osstd::string
operadores lançem exceções, o buffer alocado porFormatMessage
é vazado.4
Por que não simplesmente retornar a emstd::string
vez de fazer com que o chamador passe um objeto por referência?Desde o c ++ 11, você pode usar a biblioteca padrão em vez de
FormatMessage
:fonte
GetLastError
produz um resultado significativo. Sendo esse C ++, a única opção segura aqui é fazer com que o chamador forneça o último código de erro. Certamente não ajuda, que o código apresentado chamaGetLastError
duas vezes . Além disso, embora conveniente, a falta inerente de amplo suporte a caracteres do C ++ falha ao tornar aerror_category
interface universalmente útil. Isso apenas contribui para o longo histórico de oportunidades perdidas do C ++.GetLastError
. Mas não vejo diferença entre ligarGetLastError
aqui ou fazer com que o chamador ligue. Em relação ao C ++ e ao wchar: não abandone a esperança, a Microsoft está começando a permitir que os aplicativos sejam apenas UTF-8 .log_error("error", GetLastErrorAsString());
. Considere também quelog_error
o primeiro argumento desse tipo é do tipostd::string
. A chamada de invisibilidade (conversão invisível) acabou de abandonar suas garantias para capturar um valor significativoGetLastError
no momento em que você está chamando.malloc
solicitaHeapAlloc
o heap do processo. O heap do processo é expansível. Se ele precisa para crescer, ele acabará por chamar VirtualAlloc , que não definida último código de erro do segmento de chamada. Agora isso está totalmente errado, o que é: C ++ é um campo minado. Essa implementação apenas contribui para isso, fornecendo uma interface com garantias implícitas que ela simplesmente não pode cumprir. Se você acredita que não há problema, deve ser fácil provar a correção da solução proposta. Boa sorte.vou deixar isso aqui, pois precisarei usá-lo mais tarde. É uma fonte para uma pequena ferramenta compatível com binários que funcionará igualmente bem em montagem, C e C ++.
GetErrorMessageLib.c (compilado em GetErrorMessageLib.dll)
versão embutida (GetErrorMessage.h):
caso de uso dinâmico (assumindo que o código de erro é válido, caso contrário, é necessária uma verificação -1):
caso de uso regular (assume que o código de erro é válido, caso contrário, é necessária uma verificação de retorno -1):
exemplo usando with assembly gnu como no MinGW32 (novamente, assumindo que o código de erro é válido, caso contrário, é necessária uma verificação -1).
resultado:
The process cannot access the file because another process has locked a portion of the file.
fonte
FormatMessage
, sem motivo aparente, e se limita arbitrariamente a 80 caracteres, novamente, por nenhum motivo. Receio, isso não ajuda.ERROR_LOCK_VIOLATION
(33) é: "O processo não pode acessar o arquivo porque outro processo bloqueou uma parte do arquivo". São ambos claramente maiores que 80 caracteres e vale muito a pena ler, se você estiver tentando resolver um problema e encontrar isso em um arquivo de log de diagnóstico. Esta resposta não agrega nenhum valor substancial sobre as respostas existentes.