Comment utiliser FormatMessage () correctement en C++?

sans :

  • MFC
  • ATL

Comment puis-je utiliser FormatMessage() pour obtenir le texte d'erreur pour un 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);
 }
80
demandé sur Shog9 2009-01-18 19:43:36

6 réponses

Voici la bonne façon de récupérer un message d'erreur du système pour un HRESULT (nommé hresult dans ce cas, ou vous pouvez le remplacer par 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;
}

la principale différence entre cette réponse et celle de David Hanak est l'utilisation du drapeau FORMAT_MESSAGE_IGNORE_INSERTS . MSDN n'est pas très clair sur la façon dont les insertions devraient être utilisées, mais Raymond Chen note que vous ne devriez jamais les utiliser lors de la récupération d'un message système, car vous n'avez aucune chance de savoir quelles insertions le système attend.

FWIW, si vous utilisez Visual C++ vous pouvez rendre votre vie un peu plus facile en utilisant le _com_error classe:

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

ne fait pas partie de MFC ou ATL directement autant que je sache.

124
répondu Shog9 2015-06-24 07:11:23

gardez à l'esprit que vous ne pouvez pas faire ce qui suit:

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

comme la classe est créée et détruite sur la pile laissant errorText pour pointer vers un emplacement invalide. Dans la plupart des cas, cet emplacement contiendra toujours la chaîne d'erreur, mais cette probabilité disparaît rapidement lors de l'écriture des applications filetées.

Donc toujours faire comme suit répondu par Shog9 ci-dessus:

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}
14
répondu Marius 2009-09-16 22:15:28

essayez ceci:

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);
}
10
répondu David Hanak 2015-06-24 07:12:30

Voici une version de la fonction de David qui gère Unicode

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
répondu Oleg Zhylin 2015-02-23 19:36:48

il s'agit plus d'un ajout à la majorité des réponses, mais au lieu d'utiliser LocalFree(errorText) utilisez la fonction HeapFree :

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

du site MSDN :

fenêtres 10 :

LocalFree n'est pas dans le SDK moderne, il ne peut donc pas être utilisé pour libérer le tampon de résultat. À la place, utilisez HeapFree (GetProcessHeap(), allocatedMessage). Dans ce cas, c'est la même chose que d'appeler LocalFree en mémoire.

mise à Jour

J'ai trouvé que LocalFree est dans la version 10.0.10240.0 du SDK (ligne 1108 dans WinBase.h.) Cependant, l'avertissement existe toujours dans le lien ci-dessus.

#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

Je suggère également d'utiliser le drapeau FORMAT_MESSAGE_MAX_WIDTH_MASK pour nettoyer les interruptions de ligne dans les messages système.

du site MSDN :

FORMAT_MESSAGE_MAX_WIDTH_MASK

La fonction ignore les sauts de ligne réguliers dans le texte de définition du message. La fonction stocke les sauts de ligne codés en dur dans le texte de définition du message dans le tampon de sortie. La fonction ne génère aucun nouveau saut de ligne.

mise à jour 3

Il semble y avoir deux codes d'erreur système particuliers qui ne renvoient pas le message complet en utilisant l'approche recommandée:

pourquoi FormatMessage ne crée-t-il que des messages partiels pour ERROR_SYSTEM_PROCESS_TERMINATED et ERROR_UNHANDLED_EXCEPTION system errors?

4
répondu Class Skeleton 2017-05-23 12:26:07

le code ci-dessous est l'équivalent en C++ que j'ai écrit en contraste avec ErrorExit () de Microsoft mais légèrement modifié pour éviter toutes les macros et utiliser unicode. L'idée ici est d'éviter les fontes et les mallocs. Je n'ai pas pu échapper à tous les c casts, mais c'est le mieux que j'ai pu trouver. En ce qui concerne FormatMessageW(), qui nécessite qu'un pointeur soit affecté par la fonction format et L'identifiant D'erreur du GetLastError(). Le pointeur après static_cast peut être utilisé comme un pointeur wchar_t normal.

#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
répondu 2017-11-15 04:33:01