2009-01-18 5 views

Antwort

123

Hier ist der richtige Weg ist aus dem System eine Fehlermeldung aus, um wieder für ein HRESULT (benannt hresult in diesem Fall, oder Sie können es mit GetLastError() ersetzen):

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; 
} 

Der Hauptunterschied zwischen dieser und David Hanaks Antwort ist die Verwendung des FORMAT_MESSAGE_IGNORE_INSERTS Flags. MSDN ist ein wenig unklar, wie Einfügungen verwendet werden sollten, aber Raymond Chen notes that you should never use them beim Abrufen einer Systemnachricht, da Sie nicht wissen, welche Einfügungen das System erwartet.

FWIW, wenn Sie Visual C++ verwenden Sie Ihr Leben ein bisschen einfacher durch Verwendung der _com_error Klasse machen kann:

{ 
    _com_error error(hresult); 
    LPCTSTR errorText = error.ErrorMessage(); 

    // do something with the error... 

    //automatic cleanup when error goes out of scope 
} 

nicht direkt von MFC oder ATL Teil so weit ich bewusst bin.

+4

Vorsicht: Dieser Code verwendet hResult anstelle eines Win32-Fehlercodes: das sind verschiedene Dinge! Sie erhalten möglicherweise den Text eines vollständig anderen Fehlers als der, der tatsächlich aufgetreten ist. –

+0

Hervorragender Punkt, @Andrei - und auch wenn der Fehler * ein Win32-Fehler ist, wird diese Routine nur gelingen, wenn es sich um einen * System * -Fehler handelt - ein robuster Fehlerbehandlungsmechanismus müsste die Quelle des Fehlers kennen Fehler, überprüfen Sie den Code vor dem Aufrufen von FormatMessage und möglicherweise stattdessen andere Quellen abfragen. – Shog9

+0

@AndreiBelogortseff Wie kann ich wissen, was ich in jedem Fall verwenden soll? Zum Beispiel gibt 'RegCreateKeyEx' ein' LONG' zurück. Seine Dokumentation sagt, dass ich 'FormatMessage' verwenden kann, um den Fehler zu erhalten, aber ich muss das' LONG' in ein 'HRESULT' umwandeln. – csl

9

Try this:

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); 
} 
+0

void HandleLastError (hresult)? – Aaron

+0

Hallo David, ich würde lieber nicht MFC: "TRACE()" ... – Aaron

+1

Sicher können Sie diese Anpassungen selbst vornehmen. – oefe

13

Beachten Sie, dass Sie Folgendes nicht tun:

{ 
    LPCTSTR errorText = _com_error(hresult).ErrorMessage(); 

    // do something with the error... 

    //automatic cleanup when error goes out of scope 
} 

Da die Klasse erstellt wird und auf eine ungültige Position zeigen Fehlertext auf dem Stapel zerstört zu verlassen. In den meisten Fällen enthält dieser Speicherort weiterhin die Fehlerzeichenfolge, diese Wahrscheinlichkeit fällt jedoch beim Schreiben von Thread-Anwendungen schnell weg.

So immer es tun, wie durch Shog9 wie oben beantwortet folgt:

{ 
    _com_error error(hresult); 
    LPCTSTR errorText = error.ErrorMessage(); 

    // do something with the error... 

    //automatic cleanup when error goes out of scope 
} 
+6

Das '_com_error'-Objekt wird auf dem Stapel in * beiden * Beispielen erstellt . Der Begriff, nach dem Sie suchen, ist * temporär *. Im vorherigen Beispiel ist das Objekt ein temporäres Objekt, das am Ende der Anweisung zerstört wird. –

+0

Yup, meinte das. Aber ich würde hoffen, dass die meisten Leute das zumindest aus dem Code herausfinden könnten. Technisch Provisorien werden nicht am Ende der Anweisung zerstört, sondern am Ende des Sequenzpunktes. (was in diesem Beispiel das Gleiche ist, so dass dies nur Haare spaltet.) – Marius

+5

btw, _com_error wird in 'comdef.h' deklariert – Francois

4

Hier ist eine Version von Davids Funktion, die Unicode-Griffe

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); 

}

4

Diese ist mehr eine Ergänzung zu der Mehrheit der Antworten, aber anstattzu verwendenverwenden, um die HeapFree Funktion:

::HeapFree(::GetProcessHeap(), NULL, errorText); 

From the MSDN site:

Windows-10:
Localfree ist nicht in der modernen SDK, so kann es nicht verwendet werden, um die Ergebnispuffer zu befreien. Verwenden Sie stattdessen HeapFree (GetProcessHeap(), allocatedMessage). In diesem Fall entspricht das dem Aufruf von LocalFree im Speicher.

aktualisieren
fand ich, dass LocalFree in Version 10.0.10240.0 des SDK (Linie 1108 in winbase.h) ist. Die Warnung besteht jedoch weiterhin im obigen Link.

#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 

Update 2
ich auch die FORMAT_MESSAGE_MAX_WIDTH_MASK Flag verwendet würde vorschlagen, Zeilenumbrüche in Systemmeldungen aufzuräumen.

From the MSDN site:

FORMAT_MESSAGE_MAX_WIDTH_MASK
Die Funktion ignoriert in der Nachrichtendefinitionstext reguläre Zeilenumbrüche. Die Funktion speichert hartcodierte Zeilenumbrüche im Nachrichtendefinitionstext im Ausgabepuffer. Die Funktion erzeugt keine neuen Zeilenumbrüche.

Update 3
Es erscheint 2 bestimmte Systemfehlercodes zu sein, die die vollständige Meldung unter Verwendung der empfohlenen Ansatz nicht zurück:

Why does FormatMessage only create partial messages for ERROR_SYSTEM_PROCESS_TERMINATED and ERROR_UNHANDLED_EXCEPTION system errors?

0

Der folgende Code-Code ist die C++ Äquivalent habe ich im Gegensatz zu Microsoft's ErrorExit() aber etwas verändert geschrieben, um alle Makros zu vermeiden und Unicode zu verwenden. Die Idee hier ist, unnötige Casts und Mallocs zu vermeiden. Ich konnte nicht allen C-Casts entkommen, aber das ist das Beste, was ich aufbringen konnte. Bezieht sich auf FormatMessageW(), das erfordert, dass ein Zeiger von der Formatfunktion und der Fehler-ID von GetLastError() zugewiesen wird. Der Zeiger nach static_cast kann wie ein normaler wchar_t-Zeiger verwendet werden.

#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); 
} 
Verwandte Themen