C ++でFormatMessage()を適切に使用するにはどうすればよいですか?


89

なし

  • MFC
  • ATL

FormatMessage()のエラーテキストを取得するにはどうすればよい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);
 }

回答:


133

システムからエラーメッセージを返す適切な方法HRESULTは次のとおりです(この場合はhresultという名前ですが、に置き換えることができます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;
}

これとDavid Hanakの回答の主な違いは、FORMAT_MESSAGE_IGNORE_INSERTSフラグの使用です。MSDNは挿入がどのように使用されるべきかについて少し不明確ですが、レイモンドチェンはシステムが期待する挿入を知る方法がないので、システムメッセージを取得するときにそれらを使用してはならないことを指摘します

FWIW、Visual C ++を使用している場合は、_com_errorクラスを使用することで生活を少し楽にすることができます。

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

   //automatic cleanup when error goes out of scope
}

私が知る限り、MFCまたはATLの一部ではありません。


8
注意:このコードは、Win32エラーコードの代わりにhResultを使用します。これらは異なるものです。実際に発生したエラーとはまったく異なるエラーのテキストが表示される場合があります。
Andrei Belogortseff 2014年

1
優れた点、@ Andrei-そして実際、エラー Win32エラーであっても、このルーチンはシステムエラーの場合にのみ成功します-堅牢なエラー処理メカニズムはエラーの原因を認識している必要があり、コードを調べますFormatMessageを呼び出す前に、おそらく他のソースを照会してください。
Shog9 2014年

1
@AndreiBelogortseff各ケースで何を使用するかをどのように知ることができますか?たとえば、をRegCreateKeyEx返しますLONG。そのドキュメントにはFormatMessage、エラーを取得するために使用できると記載されていますが、をにキャストする必要LONGがありHRESULTます。
csl 2015年

FormatMessage()はDWORD、@ csl、有効なエラーコードと見なされる符号なし整数を受け取ります。すべての戻り値、またはHRESULTSが有効なエラーコードになるとは限りません。システムは、関数を呼び出す前であることを確認したと見なします。RegCreateKeyExのドキュメントでは、戻り値をエラーとして解釈できる場合を指定する必要があります... 最初にそのチェックを実行し、次にFormatMessageを呼び出します。
Shog9

1
MSDNは実際、同じバージョンのコードを提供しています。
ahmd0

14

次のことはできません。

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

クラスがスタック上で作成および破棄されると、errorTextは無効な場所を指すようになります。ほとんどの場合、この場所にはエラー文字列が含まれていますが、スレッド化されたアプリケーションを作成すると、その可能性はすぐに失われます。

したがって、上記のShog9が回答したように、常に次のようにしてください。

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

7
_com_errorオブジェクトがスタック上に作成され、両方のあなたの例。あなたが探している用語は一時的なものです。前の例では、オブジェクトは一時的であり、ステートメントの最後に破棄されます。
ロブ・ケネディ

うん、それはそれを意味しました。しかし、ほとんどの人が少なくともコードからそれを理解できることを願っています。技術的には、テンポラリーはステートメントの最後ではなく、シーケンスポイントの最後で破棄されます。(これはこの例でも同じことなので、ヘアを分割するだけです。)
マリウス

1
あなたはそれをしたい場合は、安全な(多分非常にない効率的な)あなたはC ++でこれを行うことができます:std::wstring strErrorText = _com_error(hresult).ErrorMessage();
ahmd0

12

これを試して:

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

void HandleLastError(hresult)?
アーロン

1
確かに、あなたは自分でこれらの適応を行うことができます。
oefe 2009年

@Atklin:パラメータからhresultを使用したい場合、最初の行(GetLastError())は必要ありません。
David Hanak、2009年

4
GetLastErrorはHResultを返しません。Win32エラーコードを返します。これは実際には何も処理しないので、PrintLastErrorという名前を好むかもしれません。また、FORMAT_MESSAGE_IGNORE_INSERTSを使用してください。
ロブ・ケネディ

助けてくれてありがとう:)-とても感謝しています
アーロン

5

これは大多数の回答への追加ですがLocalFree(errorText)HeapFree関数を使用する代わりに:

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

MSDNサイトから

Windows 10
LocalFreeは最新のSDKにはないため、結果バッファーを解放するために使用することはできません。代わりに、HeapFree(GetProcessHeap()、locatedMessage)を使用してください。この場合、これはメモリ上でLocalFreeを呼び出すのと同じです。

更新
私はそれが見つかったLocalFreeSDK(WinBase.hでライン1108)のバージョン10.0.10240.0です。ただし、上記のリンクにはまだ警告が表示されています。

#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フラグを
使用FORMAT_MESSAGE_MAX_WIDTH_MASKしてシステムメッセージの改行を整えることもお勧めします。

MSDNサイトから

FORMAT_MESSAGE_MAX_WIDTH_MASK
関数は、メッセージ定義テキストの通常の改行を無視します。この関数は、メッセージ定義テキストのハードコードされた改行を出力バッファーに格納します。関数は改行を生成しません。

Update 3
推奨されるアプローチを使用してもメッセージ全体を返さない特定のシステムエラーコードが2つあるようです。

FormatMessageがERROR_SYSTEM_PROCESS_TERMINATEDおよびERROR_UNHANDLED_EXCEPTIONシステムエラーの部分的なメッセージしか作成しないのはなぜですか?


4

これは、Unicodeを処理するDavidの関数のバージョンです。

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

}


1
_sntprintf_sUNICODEの場合、正しいバッファサイズを渡していないことに注意してください。この関数は文字数を受け取るため、の代わりに、_countofまたはARRAYSIZE別名をsizeof(buffer) / sizeof(buffer[0])使用しsizeofます。
ThFabba

3

c ++ 11以降、次の代わりに標準ライブラリを使用できますFormatMessage

#include <system_error>

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

2

他の回答で指摘されているように:

  • FormatMessage(通常)DWORDではなく結果を取ります。HRESULTGetLastError()
  • LocalFree によって割り当てられたメモリを解放するために必要です FormatMessage

私は上記のポイントを取り、私の答えにさらにいくつか追加しました:

  • ラップ FormatMessageクラスにて、必要に応じてメモリを割り当て、解放します
  • 演算子のオーバーロードを使用します(例 operator LPTSTR() const { return ...; }します、クラスを文字列として使用できるようにします)
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;

};

上記のコードのより完全なバージョンは、https//github.com/stephenquan/FormatMessageにあります。

上記のクラスでは、使い方は簡単です:

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

0

以下のコードは、MicrosoftのErrorExit()とは対照的に私が作成したC ++の同等のコードですが、すべてのマクロを回避し、Unicodeを使用するために少し変更されています。ここでの考え方は、不必要なキャストとmallocを回避することです。すべてのCキャストから逃れることはできませんでしたが、これは私が召集できる最高のものです。FormatMessageW()に関連し、フォーマット関数とGetLastError()からのエラーIDによってポインタを割り当てる必要があります。static_castの後のポインターは、通常のwchar_tポインターのように使用できます。

#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);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.