Windowsのシステムエラーコードからエラーメッセージを取得する方法
GetLastError()
関数から返されるシステムエラーコードに対応するエラーメッセージ文字列を取得するために、SystemMessageクラスを書きました。
定番の処理なので車輪の再発明なのは分かっていますが、新しい C++ で書きたいよね。string_view 大好き!
ってなわけで、このSystemMessageクラスは単純で安全なリソース管理をします。
// SystemMessage.hpp #ifndef SYSTEM_MESSAGE_HPP #define SYSTEM_MESSAGE_HPP #include <Windows.h> #include <string_view> #include <stdexcept> template <typename = void> class SystemMessageImpl { LPWSTR str = nullptr; std::size_t len = 0; public: explicit SystemMessageImpl(DWORD dwMessageId, DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)) { constexpr DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM; len = ::FormatMessageW(flags, nullptr, dwMessageId, dwLanguageId, reinterpret_cast<LPWSTR>(&str), 0, nullptr); if (!len) { throw std::runtime_error("エラーメッセージの取得に失敗しました。"); } } ~SystemMessageImpl() { ::LocalFree(str); } SystemMessageImpl(const SystemMessageImpl&) = delete; SystemMessageImpl& operator=(const SystemMessageImpl&) = delete; SystemMessageImpl(SystemMessageImpl&&) = delete; SystemMessageImpl& operator=(SystemMessageImpl&&) = delete; std::wstring_view view() const { return std::wstring_view(str, len); } }; using SystemMessage = SystemMessageImpl<>; #endif // SYSTEM_MESSAGE_HPP
クラステンプレートとして実装されているので、ヘッダーオンリーのライブラリとして使えます。
これにより、複数の翻訳単位での #include
ができますし、リンケージの問題を避けられます。たぶん。
また、エラーメッセージの取得に失敗した場合には例外を投げます。
Windowsのシステムエラーコードからエラーメッセージを取得する上でおそらく問題となるのは、訳が分からない FormatMessageW()
/ FormatMessage()
の仕様と、FORMAT_MESSAGE_ALLOCATE_BUFFER
の指定で確保されたバッファをどう管理するかだと思うのですが、その辺はクラスに閉じ込めてます。
本質となるエラーメッセージは、 view()
メンバ関数呼び出して wstring_view として使ってねって設計です。
ムーブ周りまで = delete してますが、必要であれば正しく定義してください。 ここでは不要ですし、実装するのも面倒なので、楽に安全側に倒しています。
以下の例は、SystemMessageクラスを使って、現在のスレッドの最後のエラーコードに対応するメッセージを取得して表示する例です。
システムエラーメッセージを正しく表示するために setlocale()
でロケールを日本語に設定しています。
#include <clocale> #include <iostream> #include "SystemMessage.hpp" int main() { std::setlocale(LC_CTYPE, "ja_JP.UTF-8"); try { SystemMessage message{ ::GetLastError() }; std::wcout << message.view(); } catch (const std::runtime_error& e) { std::wcerr << L"エラーメッセージの取得に失敗: " << e.what() << std::endl; } }
実行結果は以下の通り。GetLastError()
が返す値によってメッセージが変わります。
この操作を正しく終了しました。