Wie sollte ich FormatMessage() in C++ richtig verwenden?

Lesezeit: 7 Minuten

Wie sollte ich FormatMessage in C richtig verwenden
Aaron

Ohne:

  • MFC
  • ATL

Wie kann ich verwenden FormatMessage() um den Fehlertext für a zu erhalten HRESULT?

 HRESULT hresult = application.CreateInstance("Excel.Application");

 if (FAILED(hresult))
 {
     // what should i put here to obtain a human-readable
     // description of the error?
     exit (hresult);
 }

1643520008 819 Wie sollte ich FormatMessage in C richtig verwenden
Shog9

Hier ist der richtige Weg, um eine Fehlermeldung vom System für eine zurück zu bekommen HRESULT (in diesem Fall hresult genannt, oder Sie können es durch ersetzen GetLastError()):

LPTSTR errorText = NULL;

FormatMessage(
   // use system message tables to retrieve error text
   FORMAT_MESSAGE_FROM_SYSTEM
   // allocate buffer on local heap for error text
   |FORMAT_MESSAGE_ALLOCATE_BUFFER
   // Important! will fail otherwise, since we're not 
   // (and CANNOT) pass insertion parameters
   |FORMAT_MESSAGE_IGNORE_INSERTS,  
   NULL,    // unused with FORMAT_MESSAGE_FROM_SYSTEM
   hresult,
   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
   (LPTSTR)&errorText,  // output 
   0, // minimum size for output buffer
   NULL);   // arguments - see note 
   
if ( NULL != errorText )
{
   // ... do something with the string `errorText` - log it, display it to the user, etc.

   // release memory allocated by FormatMessage()
   LocalFree(errorText);
   errorText = NULL;
}

Der Hauptunterschied zwischen dieser und der Antwort von David Hanak ist die Verwendung von the FORMAT_MESSAGE_IGNORE_INSERTS Flagge. MSDN ist etwas unklar, wie Einfügungen verwendet werden sollten, aber Raymond Chen weist darauf hin, dass Sie sie niemals verwenden sollten beim Abrufen einer Systemnachricht, da Sie nicht wissen können, welche Einfügungen das System erwartet.

FWIW, wenn Sie Visual C++ verwenden, können Sie Ihr Leben ein bisschen einfacher machen, indem Sie die verwenden _com_error Klasse:

{
   _com_error error(hresult);
   LPCTSTR errorText = error.ErrorMessage();
   
   // do something with the error...

   //automatic cleanup when error goes out of scope
}

Soweit ich weiß, nicht direkt Teil von MFC oder ATL.

  • Achtung: Dieser Code verwendet hResult anstelle eines Win32-Fehlercodes: Das sind verschiedene Dinge! Möglicherweise erhalten Sie den Text eines völlig anderen Fehlers als den, der tatsächlich aufgetreten ist.

    – Andrei Belogortseff

    29. Oktober 14 um 23:00 Uhr

  • Ausgezeichneter Punkt, @Andrei – und zwar, auch wenn der Fehler ist ein Win32-Fehler, diese Routine wird nur erfolgreich sein, wenn es sich um einen System error – ein robuster Fehlerbehandlungsmechanismus müsste die Fehlerquelle kennen, den Code untersuchen, bevor FormatMessage aufgerufen wird, und vielleicht stattdessen andere Quellen abfragen.

    – Shog9

    30. Oktober 14 um 1:14 Uhr

  • @AndreiBelogortseff Woher weiß ich, was ich jeweils verwenden soll? Beispielsweise, RegCreateKeyEx gibt a zurück LONG. Seine Dokumente sagen, dass ich verwenden kann FormatMessage um den fehler abzurufen, aber ich muss den casten LONG In ein HRESULT.

    – kl

    18. Juni 15 um 6:20 Uhr


  • FormatMessage() nimmt ein DWORD, @csl, also eine Ganzzahl ohne Vorzeichen vermutet ein gültiger Fehlercode sein. Nicht alle Rückgabewerte – oder HRESULTS für diese Angelegenheit – sind gültige Fehlercodes; Das System geht davon aus, dass Sie dies vor dem Aufruf der Funktion überprüft haben. Die Dokumentation für RegCreateKeyEx sollte angeben, wann der Rückgabewert als Fehler interpretiert werden kann … Führen Sie diese Überprüfung durch Ersteund rufen Sie erst dann FormatMessage auf.

    – Shog9

    18. Juni 15 um 16:59 Uhr


  • MSDN bietet das jetzt tatsächlich an ihre Version irgendwie der gleiche Code.

    – ahmd0

    27. Dezember 18 um 17:54 Uhr

Beachten Sie, dass Sie Folgendes nicht tun können:

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

Während die Klasse erstellt und auf dem Stapel zerstört wird, weist errorText auf eine ungültige Position hin. In den meisten Fällen enthält dieser Speicherort immer noch die Fehlerzeichenfolge, aber diese Wahrscheinlichkeit fällt schnell weg, wenn Anwendungen mit Threads geschrieben werden.

So stets Gehen Sie wie folgt vor, wie oben von Shog9 beantwortet:

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

  • Der _com_error Objekt wird auf dem Stack in erstellt beide deine Beispiele. Der gesuchte Begriff ist vorübergehend. Im vorherigen Beispiel ist das Objekt ein temporäres Objekt, das am Ende der Anweisung zerstört wird.

    – Rob Kennedy

    16. September 2009 um 22:27 Uhr

  • Ja, das war gemeint. Aber ich würde hoffen, dass die meisten Leute das zumindest aus dem Code herausfinden können. Technisch gesehen werden Provisorien nicht am Ende der Anweisung zerstört, sondern am Ende des Sequenzpunkts. (Das ist in diesem Beispiel dasselbe, also ist das nur Haarspalterei.)

    – Marius

    18. September 2009 um 16:40 Uhr

  • Wenn du es schaffen willst sicher (vielleicht nicht sehr effizient) können Sie dies in C++ tun: std::wstring strErrorText = _com_error(hresult).ErrorMessage();

    – ahmd0

    27. Dezember 18 um 18:22 Uhr


Wie sollte ich FormatMessage in C richtig verwenden
David Hanak

Versuche dies:

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: %sn", msg, err);
        OutputDebugString(buffer); // or otherwise log it
        LocalFree(err);
}

  • void HandleLastError(result)?

    – Aaron

    18. Januar 09 um 17:05 Uhr

  • Sicherlich können Sie diese Anpassungen selbst vornehmen.

    – öfe

    18. Januar 09 um 17:12 Uhr

  • @Atklin: Wenn Sie das Ergebnis eines Parameters verwenden möchten, benötigen Sie offensichtlich nicht die erste Zeile (GetLastError()).

    – David Hanak

    18. Januar 09 um 17:28 Uhr

  • GetLastError gibt kein HResult zurück. Es gibt einen Win32-Fehlercode zurück. Könnte den Namen PrintLastError bevorzugen, da dies eigentlich nicht der Fall ist handhaben irgendetwas. Und achten Sie darauf, FORMAT_MESSAGE_IGNORE_INSERTS zu verwenden.

    – Rob Kennedy

    18. Januar 09 um 17:57 Uhr

  • Danke für eure Hilfe Jungs 🙂 – sehr geschätzt

    – Aaron

    18. Januar 09 um 18:05 Uhr

Seit c++11 können Sie stattdessen die Standardbibliothek verwenden FormatMessage:

#include <system_error>

std::string message = std::system_category().message(hr)

1643520009 715 Wie sollte ich FormatMessage in C richtig verwenden
Klasse Skelett

Dies ist eher eine Ergänzung zu den meisten Antworten, sondern eine Verwendung LocalFree(errorText) benutze die HeapFree Funktion:

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

Von der MSDN-Site:

Windows 10:
LocalFree ist nicht im modernen SDK enthalten, daher kann es nicht zum Freigeben des Ergebnispuffers verwendet werden. Verwenden Sie stattdessen HeapFree (GetProcessHeap(), selectedMessage). In diesem Fall entspricht dies dem Aufrufen von LocalFree im Speicher.

Aktualisieren

ich habe das gefunden LocalFree befindet sich in Version 10.0.10240.0 des SDK (Zeile 1108 in WinBase.h). 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

Aktualisierung 2

Ich würde auch vorschlagen, die zu verwenden FORMAT_MESSAGE_MAX_WIDTH_MASK -Flag zum Aufräumen von Zeilenumbrüchen in Systemmeldungen.

Von der MSDN-Site:

FORMAT_MESSAGE_MAX_WIDTH_MASK

Die Funktion ignoriert reguläre Zeilenumbrüche im Nachrichtendefinitionstext. Die Funktion speichert fest codierte Zeilenumbrüche im Nachrichtendefinitionstext im Ausgabepuffer. Die Funktion erzeugt keine neuen Zeilenumbrüche.

Aktualisierung 3

Es scheint 2 bestimmte Systemfehlercodes zu geben, die nicht die vollständige Nachricht mit dem empfohlenen Ansatz zurückgeben:

Warum erstellt FormatMessage nur Teilmeldungen für die Systemfehler ERROR_SYSTEM_PROCESS_TERMINATED und ERROR_UNHANDLED_EXCEPTION?

Wie sollte ich FormatMessage in C richtig verwenden
Brent

Hier ist eine Version von Davids Funktion, die Unicode verarbeitet

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) / sizeof(buffer[0]), _T("ERROR: %s: %sn"), msg, err);
    OutputDebugString(buffer);
    LocalFree(err);

}

Wie in anderen Antworten erwähnt:

  • FormatMessage nimmt ein DWORD Ergebnis nicht a HRESULT (normalerweise GetLastError()).
  • LocalFree wird benötigt, um Speicher freizugeben, der von zugewiesen wurde FormatMessage

Ich habe die obigen Punkte genommen und ein paar mehr für meine Antwort hinzugefügt:

  • Wickeln Sie die FormatMessage in einer Klasse, um Speicher nach Bedarf zuzuweisen und freizugeben
  • Verwenden Sie eine Operatorüberladung (z operator LPTSTR() const { return ...; } damit Ihre Klasse als String verwendet werden kann
class CFormatMessage
{
public:
    CFormatMessage(DWORD dwMessageId,
                   DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)) :
        m_text(NULL)
    {
        Assign(dwMessageId, dwLanguageId);
    }

    ~CFormatMessage()
    {
        Clear();
    }

    void Clear()
    {
        if (m_text)
        {
            LocalFree(m_text);
            m_text = NULL;
        }
    }

    void Assign(DWORD dwMessageId,
                DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT))
    {
        Clear();
        DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM
            | FORMAT_MESSAGE_ALLOCATE_BUFFER
            | FORMAT_MESSAGE_IGNORE_INSERTS,
        FormatMessage(
            dwFlags,
            NULL,
            dwMessageId,
            dwLanguageId,
            (LPTSTR) &m_text,
            0,
            NULL);
    }

    LPTSTR text() const { return m_text; }
    operator LPTSTR() const { return text(); }

protected:
    LPTSTR m_text;

};

Eine vollständigere Version des obigen Codes finden Sie hier: https://github.com/stephenquan/FormatMessage

Mit der obigen Klasse ist die Verwendung einfach:

    std::wcout << (LPTSTR) CFormatMessage(GetLastError()) << L"n";

.

696950cookie-checkWie sollte ich FormatMessage() in C++ richtig verwenden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy