web-dev-qa-db-ja.com

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

なし

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

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の一部ではありません。

128
Shog9

次のことはできないことに注意してください。

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

クラスが作成され、スタック上で破棄され、errorTextが無効な場所を指すようにします。ほとんどの場合、この場所には引き続きエラー文字列が含まれますが、スレッド化されたアプリケーションを作成すると、その可能性は急速に低下します。

always上記のShog9の回答に従って、次のように実行します。

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

   // do something with the error...

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

これを試して:

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

以下は、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);

}

4
Oleg Zhylin

これは回答の大部分に追加されたものですが、LocalFree(errorText)を使用する代わりにHeapFree関数を使用します。

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

MSDNサイトから

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

更新
LocalFreeがSDKのバージョン10.0.10240.0(WinBase.hの1108行目)にあることがわかりました。ただし、上記のリンクには警告が引き続き存在します。

#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

更新2
また、FORMAT_MESSAGE_MAX_WIDTH_MASKフラグは、システムメッセージの改行を整理します。

MSDNサイトから

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

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

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

4
Class Skeleton

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

#include <system_error>

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

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

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

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

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

上記のコードのより完全なバージョンをここで見つけてください: https://github.com/stephenquan/FormatMessage

上記のクラスでは、使用方法は次のとおりです。

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

以下のコードは、 MicrosoftのErrorExit() とは対照的に記述したC++の同等物ですが、すべてのマクロを避けてUnicodeを使用するようにわずかに変更されています。ここでの考え方は、不必要なキャストとmallocを避けることです。私はすべてのCキャストを逃れることはできませんでしたが、これは私が召集できる最高のものです。 FormatMessageW()に関連します。これは、format関数と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);
}
0
user7533493