Windows FormatMessage的安全/灵活的外观

时间:2009-05-10 00:13:30

标签: c++ formatmessage fastformat

我需要将FormatMessage()用于项目,但我不喜欢它可怕的界面。有没有人知道在保留替换参数的同时整理它的外观?

我刚刚阅读了second part of the FastFormat introduction,正在考虑为FormatMessage()编写扩展程序(或询问FastFormat项目团队是否已经开始工作),但是我我渴望尽快得到一些东西,所以如果还有其他不错的东西我可能会抓住它。

我想要的是能够编写如下代码:

HINSTANCE netevent = ::LoadLibrary("netevent.dll");
std::string msg = LookupError(netevent, EVENT_SERVICE_START_FAILED_II,
    "child-svr", "parent-svr", "ship happens");
::puts(msg.c_str());

哪会得到结果:

The child-svr service depends on the parent-svr service which failed to start be cause of the following error:
ship happens

我构建的当前包装器具有接口:

std::string LookupError(HINSTANCE hinst, DWORD id, ...);

这有两个问题:

  • 这不是类型安全的,因为很容易传递任何类型 - intstd::stringvoid* - 这不是const char*
  • 很容易将参数数量与表示错误的格式字符串所需数量不匹配

鉴于FastFormat在类型安全方面的能力,我想知道是否有办法遵循其机制来处理FormatMessage()

2 个答案:

答案 0 :(得分:1)

由于编译器无法检查要插入格式字符串的参数数量,因此无法在编译时使其真正具有类型安全性。

只需对不同数量的插入参数进行一些重载,然后使用像boost::any这样灵活的东西指定插入的值,就可以获得大部分路径。所以两个参数的重载是:

std::string FormatMessage(HINSTANCE hinst, DWORD id, const boost::any &arg1, const boost::any &arg2);

当您从arg1检索值时,如果您尝试获取错误的类型,则会抛出boost,因此您只需要检查格式字符串并尝试从每个参数中获取所需的类型。

或者你可以使用模板和std :: ostringstream(或boost :: lexical_cast)来获得一个非常灵活的版本;再次存在重载以允许参数的数量变化,因此这里是单参数版本:

template <class TArg1>
std::string FormatMessage(HINSTANCE hinst, DWORD id, const TArg1 &arg1)
{
    std::ostringstream arg1Stream;
    arg1Stream << arg1;
    std::string arg1String = arg1Stream.str();

    DWORD_PTR argArray = reinterpret_cast<DWORD_PTR>(arg1String.c_str());

    // ... etc
}

这样,只要传递的类型可以流式传输,就可以从每个参数中获取一个字符串,只要格式字符串只需要插入字符串,就不需要任何其他内容。

答案 1 :(得分:0)

The C++ Format library允许格式化与GetLastError()返回的错误代码对应的本机Windows错误消息以及与errno给出的错误对应的POSIX错误消息。例如:

// This throws a WindowsError with the description
//   cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR)
  throw fmt::WindowsError(GetLastError(), "cannot open file '{}'", filename);

使用FormatMessage API函数获取Windows错误消息。您还可以使用fmt::format_windows_error格式化错误消息,该消息不会引发异常。有关详细信息,请参阅System Errors

免责声明:我是C ++格式的作者