Früher gab es im Tool-Bereich von Visual Studio eine exe-Fehlersuche, die dies ziemlich gut macht, wenn Sie nur eine Fehlermeldung zum Debuggen benötigen.
ColdCat
@ColdCat: Zum Debuggen ist es viel einfacher, einfach eine @err,hrUhr hinzuzufügen und den Debugger den letzten Fehlercode automatisch in eine für Menschen lesbare Darstellung konvertieren zu lassen. Der ,hrFormatbezeichner funktioniert für jeden Ausdruck, der einen ganzzahligen Wert ergibt, z. B. zeigt eine 5,hrUhr "ERROR_ACCESS_DENIED: Zugriff verweigert" an. .
Unsichtbarer
2
Aus der GetLastError()Dokumentation: " Verwenden Sie die FormatMessage()Funktion, um eine Fehlerzeichenfolge für Systemfehlercodes zu erhalten . " Siehe das Beispiel zum Abrufen des Codes für den letzten Fehler in MSDN.
Remy Lebeau
Antworten:
145
//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;}
Ich glaube, Sie müssen (LPSTR)&messageBufferin diesem Fall tatsächlich übergeben , da FormatMessageA sonst seinen Wert nicht so ändern kann, dass er auf den zugewiesenen Puffer verweist.
Kylotan
2
Oh, wow, ja das ist irgendwie komisch. Wie würde es den Zeiger ändern? Aber die Adresse des Zeigers (Zeiger auf einen Zeiger) übergeben, aber in einen regulären Zeiger umwandeln ... Win32-Verrücktheit. Vielen Dank für das Heads-up, das in meiner eigenen Codebasis (und meiner Antwort) behoben wurde. Sehr subtiler Fang.
Jamin Gray
1
Vielen Dank, Ihr Beispiel ist viel klarer als das von MSDN. Darüber hinaus konnte der von MSDN nicht einmal kompiliert werden. Es enthält einige strsafe.hHeader, die überhaupt nicht sicher sind und eine Reihe von Compilerfehlern in winuser.hund verursachen winbase.h.
Hi-Angel
2
Einige Fehler-IDs werden nicht unterstützt. Beispiel: 0x2EE7, ERROR_INTERNET_NAME_NOT_RESOLVED verursacht beim Aufrufen von FormatMessage einen neuen Fehler: 0x13D. Das System kann den Nachrichtentext für die Nachrichtennummer 0x% 1 in der Nachrichtendatei für% 2 nicht finden.
Brent
3
Probleme mit dieser Implementierung: 1GetLastErrorwird möglicherweise zu spät aufgerufen. 2Keine Unicode-Unterstützung. 3Verwendung von Ausnahmen ohne Implementierung von Ausnahmesicherheitsgarantien.
Unsichtbarer
65
Aktualisiert (11/2017), um einige Kommentare zu berücksichtigen.
@ Hi-Angel - Das Beispiel setzt voraus, dass Sie mit UNICODE definiert kompilieren. 'FormatMessage' ist eigentlich ein Makro, das je nach Kompilierung der Anwendung entweder zu 'FormatMessageA' für Ansi / MBCS-Zeichenpuffer oder zu 'FormatMessageW' für UTF16 / UNICODE-Puffer erweitert wird. Ich habe mir erlaubt, das obige Beispiel zu bearbeiten, um explizit die Version aufzurufen, die dem Ausgabepuffertyp (wchar_t) entspricht.
Bukes
2
Fügen Sie FORMAT_MESSAGE_IGNORE_INSERTS hinzu: "Wenn Sie nicht die Kontrolle über die Formatzeichenfolge haben, müssen Sie FORMAT_MESSAGE_IGNORE_INSERTS übergeben, um zu verhindern, dass% 1 Probleme verursacht." blogs.msdn.microsoft.com/oldnewthing/20071128-00/?p=24353
Roi Danton
1
Probleme mit dieser Implementierung: 1Fehler beim Angeben des wichtigen FORMAT_MESSAGE_IGNORE_INSERTSFlags. 2GetLastErrormöglicherweise zu spät angerufen. 3Beliebige Beschränkung der Nachricht auf 256 Codeeinheiten. 4Keine Fehlerbehandlung.
Unsichtbarer
1
sizeof (buf) sollte ARRAYSIZE (buf) sein, da FormatMessage die Größe des Puffers in TCHARs und nicht in Bytes erwartet.
Kai
1
Können wir nicht 256anstelle von verwenden (sizeof(buf) / sizeof(wchar_t)? ist es akzeptabel und sicher?
GetLastError gibt einen numerischen Fehlercode zurück. Um eine beschreibende Fehlermeldung zu erhalten (z. B. um sie einem Benutzer anzuzeigen), können Sie FormatMessage aufrufen :
// 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);}
In C ++ können Sie die Schnittstelle mithilfe der Klasse std :: string erheblich vereinfachen:
#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.");}}
HINWEIS: Diese Funktionen funktionieren auch für HRESULT-Werte. Ändern Sie einfach den ersten Parameter von DWORD dwErrorCode in HRESULT hResult. Der Rest des Codes kann unverändert bleiben.
Diese Implementierungen bieten die folgenden Verbesserungen gegenüber den vorhandenen Antworten:
Vollständiger Beispielcode, nicht nur ein Verweis auf die aufzurufende API.
Bietet sowohl C- als auch C ++ - Implementierungen.
Funktioniert sowohl für Unicode- als auch für MBCS-Projekteinstellungen.
Nimmt den Fehlercode als Eingabeparameter. Dies ist wichtig, da der letzte Fehlercode eines Threads nur an genau definierten Punkten gültig ist. Ein Eingabeparameter ermöglicht es dem Anrufer, dem dokumentierten Vertrag zu folgen.
Implementiert die richtige Ausnahmesicherheit. Im Gegensatz zu allen anderen Lösungen, die implizit Ausnahmen verwenden, geht bei dieser Implementierung kein Speicher verloren, falls beim Erstellen des Rückgabewerts eine Ausnahme ausgelöst wird.
Richtige Fehlerbehandlung / Fehlerberichterstattung, im Gegensatz zu einigen anderen Antworten, bei denen Fehler stillschweigend ignoriert werden.
Diese Antwort wurde aus der Stapelüberlaufdokumentation übernommen. Die folgenden Benutzer haben zu dem Beispiel beigetragen: stackptr , Ajay , Cody Gray ♦ , IInspectable .
Anstatt zu werfen std::runtime_error, schlage ich vor, zu werfen, std::system_error(lastError, std::system_category(), "Failed to retrieve error message string.")wo lastErrorder Rückgabewert GetLastError()nach dem fehlgeschlagenen FormatMessage()Aufruf wäre.
Zett42
Was ist der Vorteil einer FormatMessagedirekten Verwendung Ihrer C-Version ?
Micha Wiedenmann
@mic: Das ist wie zu fragen: "Was ist der Vorteil von Funktionen in C?" Funktionen reduzieren die Komplexität durch Bereitstellung von Abstraktionen. In diesem Fall reduziert die Abstraktion die Anzahl der Parameter von 7 auf 3, implementiert den dokumentierten Vertrag der API durch Übergeben kompatibler Flags und Parameter und codiert die dokumentierte Fehlerberichterstattungslogik. Es bietet eine übersichtliche Oberfläche mit leicht zu erratender Semantik, sodass Sie nicht 11 Minuten investieren müssen, um die Dokumentation zu lesen .
Unsichtbarer
Ich mag diese Antwort, es ist sehr klar, dass der Richtigkeit des Codes besondere Aufmerksamkeit geschenkt wurde. Ich habe zwei Fragen. 1) Warum verwenden Sie ::HeapFree(::GetProcessHeap(), 0, p)den Deleter anstelle ::LocalFree(p)der Dokumentation? 2) Mir ist klar, dass die Dokumentation dies so vorschreibt, aber nicht reinterpret_cast<LPTSTR>(&psz)gegen die strenge Aliasing-Regel verstößt?
JoE
@teh: Danke für das Feedback. Frage 1): Ehrlich gesagt weiß ich nicht, ob dies sicher ist. Ich erinnere mich nicht, wer oder warum der Aufruf an HeapFreeeingeführt wurde, während dies noch ein Thema in der SO-Dokumentation war. Ich muss nachforschen (obwohl die Dokumentation klar zu sein scheint, dass dies nicht sicher ist). Frage 2): Dies ist zweifach. Ist für eine MBCS-Konfiguration LPTSTRein Alias für char*. Diese Besetzung ist immer sicher. Bei einer Unicode-Konfiguration ist das Casting wchar_t*auf jeden Fall faul. Ich weiß nicht, ob dies in C sicher ist, aber es ist höchstwahrscheinlich nicht in C ++. Ich würde das auch untersuchen müssen.
Unsichtbarer
13
Im Allgemeinen müssen Sie verwenden FormatMessage , um von einem Win32-Fehlercode in Text zu konvertieren.
Formatiert eine Nachrichtenzeichenfolge. Die Funktion erfordert eine Nachrichtendefinition als Eingabe. Die Nachrichtendefinition kann aus einem Puffer stammen, der an die Funktion übergeben wird. Es kann aus einer Nachrichtentabellenressource in einem bereits geladenen Modul stammen. Oder der Anrufer kann die Funktion auffordern, die Nachrichtentabellenressourcen des Systems nach der Nachrichtendefinition zu durchsuchen. Die Funktion findet die Nachrichtendefinition in einer Nachrichtentabellenressource basierend auf einer Nachrichtenkennung und einer Sprachkennung. Die Funktion kopiert den formatierten Nachrichtentext in einen Ausgabepuffer und verarbeitet auf Anfrage alle eingebetteten Einfügesequenzen.
Ich habe bestätigt, dass dieser Code funktioniert. Sollte TS diese Antwort nicht akzeptieren?
Swdev
2
Wenn es für ein weiteres Werfen notwendig ist, gibt es eine einfachere Möglichkeit , dies in C # mit Win32Exception
SerG
2
@swdev: Warum sollte jemand eine Antwort in C # auf eine Frage mit dem Tag c oder c ++ akzeptieren ? Diese Antwort geht derzeit nicht einmal auf die gestellte Frage ein.
Unsichtbarer
1
Ich kann mich nicht erinnern, über diese vorgeschlagene Antwort abgestimmt zu haben. Ich habe den offensichtlichen Fehler in der Logik von @ swdev angesprochen. Aber da Sie mir nicht glauben werden, werde ich es Ihnen jetzt beweisen: Hier, haben Sie eine weitere Ablehnung. Diese ist von mir, weil diese Antwort - obwohl sie bei einer anderen Frage nützlich sein kann - angesichts der gestellten Frage einfach nicht nützlich ist.
Unsichtbarer
2
"Ich denke, Sie haben hilfreiche Einblicke, die über das Offensichtliche hinausgehen" - in der Tat .
Unsichtbarer
4
Wenn Sie sowohl MBCS als auch Unicode unterstützen müssen, reicht die Antwort von Mr.C64 nicht aus. Der Puffer muss als TCHAR deklariert und in LPTSTR umgewandelt werden. Beachten Sie, dass dieser Code nicht die nervige neue Zeile behandelt, die Microsoft an die Fehlermeldung anfügt.
Falls der CStringc'tor eine Ausnahme auslöst, verliert diese Implementierung den durch den Aufruf an zugewiesenen Speicher FormatMessage.
Unsichtbarer
Stimmt, aber ich habe diesen Code seit vielen Jahren verwendet und es war nie ein Problem. Der einzige Fall, in dem der CString-Ctor wahrscheinlich einen Fehler verursacht, ist ein Fehler beim Zuweisen von Speicher, und der meiste MFC-Code - einschließlich des von Microsoft bereitgestellten Materials - verarbeitet nicht genügend Speicherbedingungen, wie Sie möchten. Glücklicherweise haben die meisten PCs jetzt so viel Speicher, dass Sie ziemlich hart arbeiten müssen, um alles zu nutzen. Bei jeder Verwendung, die eine temporäre CString-Instanz generiert (einschließlich der Rückgabe eines CString), besteht dieses Risiko. Es ist ein Kompromiss zwischen Risiko und Zweckmäßigkeit. Auch wenn es in einem Nachrichtenhandler passiert, wird die Ausnahme abgefangen.
Opfer der Freizeit
Der größte Teil dieses Kommentars ist falsch, sorry. "Mir ist es nie passiert" ist eine verdammte Schwachstelle, besonders wenn Sie wissen, wie der Code fehlschlagen kann. Die Speichermenge hat auch keinen Einfluss auf den verfügbaren Adressraum, der einem Prozess zugewiesen ist. RAM ist nur eine Leistungsoptimierung. Copy-Elision verhindert die Zuweisung eines temporären Elements, wenn Code geschrieben wird, um NRVO zu ermöglichen. Ob Ausnahmen in einem Nachrichtenhandler abgefangen werden oder nicht, hängt von der Prozessbitness und den externen Einstellungen ab. Ich habe eine Antwort eingereicht, die zeigt, dass Risikomanagement nicht zu Unannehmlichkeiten führt.
Unsichtbarer
Außerdem besteht das Risiko, dass beim Erstellen eines temporären Objekts nicht genügend Speicherplatz zur Verfügung steht. Zu diesem Zeitpunkt wurden alle Ressourcen freigegeben, und es wird nichts Schlimmes daraus.
Unsichtbarer
1
Nein, es tut mir leid, dass der Code für Sie nicht nützlich ist. Aber TBH ist ziemlich niedrig auf meiner Liste der Probleme.
Opfer der Freizeit
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);}}
Probleme mit dieser Implementierung: 1Keine Unicode-Unterstützung. 2Unangemessene Formatierung der Fehlermeldung. Wenn der Aufrufer die zurückgegebene Zeichenfolge verarbeiten muss, kann er dies einfach tun. Ihre Implementierung lässt dem Anrufer keine Option. 3Verwendung von Ausnahmen, aber mangelnde angemessene Ausnahmesicherheit. Falls die std::stringOperatoren Ausnahmen auslösen, ist der von zugewiesene Puffer FormatMessagedurchgesickert. 4Warum nicht einfach ein zurückgeben, std::stringanstatt den Anrufer ein Objekt als Referenz übergeben zu lassen?
Unsichtbarer
1
Seit c ++ 11 können Sie die Standardbibliothek anstelle von 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);}}
Es gibt nur ein winziges Fenster, in dem das Aufrufen GetLastErrorein aussagekräftiges Ergebnis liefert. Da dies C ++ ist, besteht die einzig sichere Option darin, dass der Anrufer den letzten Fehlercode bereitstellt. Es hilft sicherlich nicht, dass der präsentierte Code GetLastErrorzweimal aufruft . Auch wenn dies praktisch ist, macht das Fehlen einer breiten Zeichenunterstützung in C ++ die error_categoryBenutzeroberfläche nicht universell nützlich. Dies trägt nur zu C ++ 'langer Geschichte verpasster Gelegenheiten bei.
Unsichtbar
Guter Punkt über den nutzlosen Anruf bei GetLastError. Aber ich sehe keinen Unterschied zwischen einem Anruf GetLastErrorhier oder einem Anruf des Anrufers. In Bezug auf C ++ und wchar: Geben Sie die Hoffnung nicht auf, Microsoft beginnt damit, Apps nur UTF-8 zuzulassen .
Chronial
"Ich sehe keinen Unterschied" - Betrachten Sie die folgende Anrufseite : log_error("error", GetLastErrorAsString());. Bedenken Sie auch, dass log_errordas erste Argument vom Typ ist std::string. Der (unsichtbare) Conversion-Aufruf hat gerade Ihre Garantien gelöscht, um einen aussagekräftigen Wert GetLastErroran dem Punkt zu erfassen, an dem Sie ihn aufrufen.
Unsichtbar
Warum ruft der Konstruktor std :: string Ihrer Meinung nach eine Win32-Funktion auf?
Chronial
1
mallocruft HeapAllocnach dem Prozessheap. Der Prozesshaufen ist erweiterbar. Wenn es wachsen muss, wird es nennen letztlich VirtualAlloc , die sich der anrufenden Thread des letzten Fehlercode gesetzt. Nun, das ist völlig verfehlt: C ++ ist ein Minenfeld. Diese Implementierung trägt nur dazu bei, indem sie eine Schnittstelle mit impliziten Garantien bereitstellt, denen sie einfach nicht gerecht werden kann. Wenn Sie der Meinung sind, dass es kein Problem gibt, sollten Sie die Richtigkeit der vorgeschlagenen Lösung leicht nachweisen können . Viel Glück.
Unsichtbar
0
Ich werde dies hier lassen, da ich es später verwenden muss. Es ist eine Quelle für ein kleines binär kompatibles Tool, das in Assembly, C und C ++ gleich gut funktioniert.
GetErrorMessageLib.c (kompiliert zu 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;}}
Inline-Version (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 */
dynamischer Anwendungsfall (vorausgesetzt, der Fehlercode ist gültig, andernfalls ist eine -1-Prüfung erforderlich):
Beispiel für die Verwendung mit Assembly Gnu wie in MinGW32 (wiederum vorausgesetzt, dass der Fehlercode gültig ist, andernfalls ist eine Überprüfung von -1 erforderlich).
Dies fügt wirklich nichts Nützliches hinzu. Darüber hinaus ruft es die ANSI-Version von FormatMessageohne ersichtlichen Grund auf und beschränkt sich willkürlich auf 80 Zeichen, wiederum ohne jeglichen Grund. Ich fürchte, das ist nicht hilfreich.
Unsichtbar
Sie haben Recht, ich hatte gehofft, niemand würde etwas über das Fehlen der Unicode-Version bemerken. Ich werde überprüfen, wie eine Unicode-Zeichenfolge in gnu als definiert wird, und meine Lösung ändern. Entschuldigung für die Unehrlichkeit.
Dmitry
ok Unicode-Version ist aktiv. und es ist kein willkürlicher Grund; Alle Fehlermeldungen bestehen entweder aus weniger als 80 Zeichen oder sind nicht lesenswert. Der Fehlercode ist wichtiger als die Fehlermeldung. Es gibt keine Standardfehlermeldungen mit mehr als 80 Zeichen, daher ist dies eine sichere Annahme, und wenn dies nicht der Fall ist, geht kein Speicher verloren.
Dmitry
3
"Alle Fehlermeldungen sind entweder weniger als 80 Zeichen oder nicht lesenswert" - Das ist falsch. Die Fehlermeldung für ERROR_LOCK_VIOLATION(33) lautet: "Der Prozess kann nicht auf die Datei zugreifen, da ein anderer Prozess einen Teil der Datei gesperrt hat." Das ist deutlich länger als 80 Zeichen und sehr lesenswert, wenn Sie versuchen, ein Problem zu lösen und es in einer Diagnoseprotokolldatei zu finden. Diese Antwort bietet keinen wesentlichen Mehrwert gegenüber den vorhandenen Antworten.
IInspectable
Das ist nicht weniger naiv. Es scheitert nur seltener. Mit Ausnahme der Assembly-Implementierung, die sich auf die Puffergröße bezieht (behauptet, Platz für 200 Zeichen zu haben, wenn sie nur Platz für 100 hat). Auch dies fügt nichts Wesentliches hinzu, was in keiner der anderen Antworten bereits enthalten ist. Insbesondere ist es schlimmer als diese Antwort . Sehen Sie sich dort die Aufzählungsliste an und beobachten Sie, welche Probleme Ihre vorgeschlagene Implementierung zusätzlich zu den gerade erwähnten hat.
@err,hr
Uhr hinzuzufügen und den Debugger den letzten Fehlercode automatisch in eine für Menschen lesbare Darstellung konvertieren zu lassen. Der,hr
Formatbezeichner funktioniert für jeden Ausdruck, der einen ganzzahligen Wert ergibt, z. B. zeigt eine5,hr
Uhr "ERROR_ACCESS_DENIED: Zugriff verweigert" an. .GetLastError()
Dokumentation: " Verwenden Sie dieFormatMessage()
Funktion, um eine Fehlerzeichenfolge für Systemfehlercodes zu erhalten . " Siehe das Beispiel zum Abrufen des Codes für den letzten Fehler in MSDN.Antworten:
quelle
(LPSTR)&messageBuffer
in diesem Fall tatsächlich übergeben , da FormatMessageA sonst seinen Wert nicht so ändern kann, dass er auf den zugewiesenen Puffer verweist.strsafe.h
Header, die überhaupt nicht sicher sind und eine Reihe von Compilerfehlern inwinuser.h
und verursachenwinbase.h
.1
GetLastError
wird möglicherweise zu spät aufgerufen.2
Keine Unicode-Unterstützung.3
Verwendung von Ausnahmen ohne Implementierung von Ausnahmesicherheitsgarantien.Aktualisiert (11/2017), um einige Kommentare zu berücksichtigen.
Einfaches Beispiel:
quelle
1
Fehler beim Angeben des wichtigenFORMAT_MESSAGE_IGNORE_INSERTS
Flags.2
GetLastError
möglicherweise zu spät angerufen.3
Beliebige Beschränkung der Nachricht auf 256 Codeeinheiten.4
Keine Fehlerbehandlung.256
anstelle von verwenden(sizeof(buf) / sizeof(wchar_t)
? ist es akzeptabel und sicher?MSDN verfügt über einen Beispielcode, der die Verwendung
FormatMessage()
undGetLastError()
Zusammenführung veranschaulicht : Abrufen des Codes für den letzten Fehlerquelle
Format schaltet GetLastError ‚s integer Rückkehr in eine Textnachricht.
quelle
GetLastError gibt einen numerischen Fehlercode zurück. Um eine beschreibende Fehlermeldung zu erhalten (z. B. um sie einem Benutzer anzuzeigen), können Sie FormatMessage aufrufen :
In C ++ können Sie die Schnittstelle mithilfe der Klasse std :: string erheblich vereinfachen:
HINWEIS: Diese Funktionen funktionieren auch für HRESULT-Werte. Ändern Sie einfach den ersten Parameter von DWORD dwErrorCode in HRESULT hResult. Der Rest des Codes kann unverändert bleiben.
Diese Implementierungen bieten die folgenden Verbesserungen gegenüber den vorhandenen Antworten:
FORMAT_MESSAGE_IGNORE_INSERTS
Flagge. Weitere Informationen finden Sie unter Die Bedeutung des FORMAT_MESSAGE_IGNORE_INSERTS-Flags .Diese Antwort wurde aus der Stapelüberlaufdokumentation übernommen. Die folgenden Benutzer haben zu dem Beispiel beigetragen: stackptr , Ajay , Cody Gray ♦ , IInspectable .
quelle
std::runtime_error
, schlage ich vor, zu werfen,std::system_error(lastError, std::system_category(), "Failed to retrieve error message string.")
wolastError
der RückgabewertGetLastError()
nach dem fehlgeschlagenenFormatMessage()
Aufruf wäre.FormatMessage
direkten Verwendung Ihrer C-Version ?::HeapFree(::GetProcessHeap(), 0, p)
den Deleter anstelle::LocalFree(p)
der Dokumentation? 2) Mir ist klar, dass die Dokumentation dies so vorschreibt, aber nichtreinterpret_cast<LPTSTR>(&psz)
gegen die strenge Aliasing-Regel verstößt?HeapFree
eingeführt wurde, während dies noch ein Thema in der SO-Dokumentation war. Ich muss nachforschen (obwohl die Dokumentation klar zu sein scheint, dass dies nicht sicher ist). Frage 2): Dies ist zweifach. Ist für eine MBCS-KonfigurationLPTSTR
ein Alias fürchar*
. Diese Besetzung ist immer sicher. Bei einer Unicode-Konfiguration ist das Castingwchar_t*
auf jeden Fall faul. Ich weiß nicht, ob dies in C sicher ist, aber es ist höchstwahrscheinlich nicht in C ++. Ich würde das auch untersuchen müssen.Im Allgemeinen müssen Sie verwenden
FormatMessage
, um von einem Win32-Fehlercode in Text zu konvertieren.Aus der MSDN- Dokumentation :
Die Deklaration von FormatMessage:
quelle
Wenn Sie c # verwenden, können Sie diesen Code verwenden:
quelle
Wenn Sie sowohl MBCS als auch Unicode unterstützen müssen, reicht die Antwort von Mr.C64 nicht aus. Der Puffer muss als TCHAR deklariert und in LPTSTR umgewandelt werden. Beachten Sie, dass dieser Code nicht die nervige neue Zeile behandelt, die Microsoft an die Fehlermeldung anfügt.
Der Kürze halber finde ich auch die folgende Methode nützlich:
quelle
CString
c'tor eine Ausnahme auslöst, verliert diese Implementierung den durch den Aufruf an zugewiesenen SpeicherFormatMessage
.quelle
1
Keine Unicode-Unterstützung.2
Unangemessene Formatierung der Fehlermeldung. Wenn der Aufrufer die zurückgegebene Zeichenfolge verarbeiten muss, kann er dies einfach tun. Ihre Implementierung lässt dem Anrufer keine Option.3
Verwendung von Ausnahmen, aber mangelnde angemessene Ausnahmesicherheit. Falls diestd::string
Operatoren Ausnahmen auslösen, ist der von zugewiesene PufferFormatMessage
durchgesickert.4
Warum nicht einfach ein zurückgeben,std::string
anstatt den Anrufer ein Objekt als Referenz übergeben zu lassen?Seit c ++ 11 können Sie die Standardbibliothek anstelle von
FormatMessage
:quelle
GetLastError
ein aussagekräftiges Ergebnis liefert. Da dies C ++ ist, besteht die einzig sichere Option darin, dass der Anrufer den letzten Fehlercode bereitstellt. Es hilft sicherlich nicht, dass der präsentierte CodeGetLastError
zweimal aufruft . Auch wenn dies praktisch ist, macht das Fehlen einer breiten Zeichenunterstützung in C ++ dieerror_category
Benutzeroberfläche nicht universell nützlich. Dies trägt nur zu C ++ 'langer Geschichte verpasster Gelegenheiten bei.GetLastError
. Aber ich sehe keinen Unterschied zwischen einem AnrufGetLastError
hier oder einem Anruf des Anrufers. In Bezug auf C ++ und wchar: Geben Sie die Hoffnung nicht auf, Microsoft beginnt damit, Apps nur UTF-8 zuzulassen .log_error("error", GetLastErrorAsString());
. Bedenken Sie auch, dasslog_error
das erste Argument vom Typ iststd::string
. Der (unsichtbare) Conversion-Aufruf hat gerade Ihre Garantien gelöscht, um einen aussagekräftigen WertGetLastError
an dem Punkt zu erfassen, an dem Sie ihn aufrufen.malloc
ruftHeapAlloc
nach dem Prozessheap. Der Prozesshaufen ist erweiterbar. Wenn es wachsen muss, wird es nennen letztlich VirtualAlloc , die sich der anrufenden Thread des letzten Fehlercode gesetzt. Nun, das ist völlig verfehlt: C ++ ist ein Minenfeld. Diese Implementierung trägt nur dazu bei, indem sie eine Schnittstelle mit impliziten Garantien bereitstellt, denen sie einfach nicht gerecht werden kann. Wenn Sie der Meinung sind, dass es kein Problem gibt, sollten Sie die Richtigkeit der vorgeschlagenen Lösung leicht nachweisen können . Viel Glück.Ich werde dies hier lassen, da ich es später verwenden muss. Es ist eine Quelle für ein kleines binär kompatibles Tool, das in Assembly, C und C ++ gleich gut funktioniert.
GetErrorMessageLib.c (kompiliert zu GetErrorMessageLib.dll)
Inline-Version (GetErrorMessage.h):
dynamischer Anwendungsfall (vorausgesetzt, der Fehlercode ist gültig, andernfalls ist eine -1-Prüfung erforderlich):
regulärer Anwendungsfall (setzt voraus, dass der Fehlercode gültig ist, andernfalls ist eine Überprüfung der Rückgabe -1 erforderlich):
Beispiel für die Verwendung mit Assembly Gnu wie in MinGW32 (wiederum vorausgesetzt, dass der Fehlercode gültig ist, andernfalls ist eine Überprüfung von -1 erforderlich).
Ergebnis:
The process cannot access the file because another process has locked a portion of the file.
quelle
FormatMessage
ohne ersichtlichen Grund auf und beschränkt sich willkürlich auf 80 Zeichen, wiederum ohne jeglichen Grund. Ich fürchte, das ist nicht hilfreich.ERROR_LOCK_VIOLATION
(33) lautet: "Der Prozess kann nicht auf die Datei zugreifen, da ein anderer Prozess einen Teil der Datei gesperrt hat." Das ist deutlich länger als 80 Zeichen und sehr lesenswert, wenn Sie versuchen, ein Problem zu lösen und es in einer Diagnoseprotokolldatei zu finden. Diese Antwort bietet keinen wesentlichen Mehrwert gegenüber den vorhandenen Antworten.