it-swarm.com.de

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

Ohne:

  • MFC
  • ATL

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

 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);
 }
82
Aaron

So erhalten Sie eine Fehlermeldung vom System für eine HRESULT (in diesem Fall mit dem Namen hresult genannt, oder Sie können sie durch 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 der Antwort von David Hanak ist die Verwendung des FORMAT_MESSAGE_IGNORE_INSERTS-Flags. MSDN ist ein wenig 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, welche Einfügungen das System erwartet. 

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

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

Soweit mir bekannt ist, kein direkter Bestandteil von MFC oder ATL. 

126
Shog9

Denken Sie daran, 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
}

Wenn die Klasse auf dem Stack erstellt und zerstört wird, lässt errorText auf einen ungültigen Speicherort verweisen. In den meisten Fällen enthält diese Position immer noch die Fehlerzeichenfolge, diese Wahrscheinlichkeit fällt jedoch beim Schreiben von Thread-Anwendungen schnell weg.

Also always mache es wie von Shog9 oben geantwortet:

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}
14
Marius

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: %s\n", msg, err);
        OutputDebugString(buffer); // or otherwise log it
        LocalFree(err);
}
11
David Hanak

Hier ist eine Version von Davids Funktion, die Unicode behandelt

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
Oleg Zhylin

Dies ist eher eine Ergänzung der meisten Antworten, aber statt LocalFree(errorText) verwenden Sie die HeapFree-Funktion:

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

Von der MSDN-Site :

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

Update
Ich habe festgestellt, dass LocalFree in Version 10.0.10240.0 des SDK (Zeile 1108 in WinBase.h) ist. Die Warnung existiert 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 würde auch vorschlagen, das FORMAT_MESSAGE_MAX_WIDTH_MASK-Flag zu verwenden, um Zeilenumbrüche in Systemmeldungen aufzuräumen.

Von der MSDN-Site

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

Update 3
Es scheint, dass es 2 bestimmte Systemfehlercodes gibt, die nicht die vollständige Nachricht mit dem empfohlenen Ansatz zurückgeben:

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

4
Class Skeleton

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

#include <system_error>

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

Der folgende Code ist der C++ - Code, den ich im Gegensatz zu Microsofts ErrorExit () geschrieben habe, aber leicht geändert, um alle Makros zu vermeiden und Unicode zu verwenden. Die Idee hier ist, unnötige Besetzungen und Mallocs zu vermeiden. Ich konnte nicht allen C-Casts entkommen, aber dies ist das Beste, was ich sammeln konnte. Betrifft FormatMessageW (), das erfordert, dass ein Zeiger von der Formatfunktion und die 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);
}
0
user7533493

Wie in anderen Antworten ausgeführt:

  • FormatMessage nimmt ein DWORD-Ergebnis entgegen, nicht eine HRESULT (normalerweise GetLastError()).
  • LocalFree wird benötigt, um den von FormatMessage zugewiesenen Speicher freizugeben.

Ich nahm die obigen Punkte und fügte ein paar mehr für meine Antwort hinzu:

  • Schließen Sie die FormatMessage in eine Klasse ein, um den Speicher nach Bedarf automatisch zuzuweisen und freizugeben
  • Verwenden Sie die Operatorüberladung (z. B. operator LPTSTR() const { return ...; }), damit Ihre Klasse als Zeichenfolge verwendet werden kann
class CFormatMessage
{
public:
    CFormatMessage(DWORD dwError) : m_ErrorText(NULL) { Assign(dwError); }
    ~CFormatMessage() { Clear(); }
    void Clear() { if (m_ErrorText != NULL) { LocalFree(m_ErrorText); m_ErrorText = NULL; } }
    void Assign(DWORD dwError) {
        Clear();
        FormatMessage(
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            dwError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPTSTR)&m_ErrorText,
            0,
            NULL);
    }
    LPTSTR ErrorText() const { return m_ErrorText; }
    operator LPTSTR() const { return ErrorText(); }
protected:
    LPTSTR m_ErrorText;
};

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

Bei der obigen Klasse ist die Verwendung einfach:

    std::wcout << (LPTSTR) CFormatMessage(GetLastError()) << L"\n";
0
Stephen Quan