【问题标题】:Safe/flexible facade for Windows FormatMessageWindows FormatMessage 的安全/灵活外观
【发布时间】:2010-10-25 01:40:22
【问题描述】:

我需要在一个项目中使用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()

【问题讨论】:

  • 您希望能够编写类似...的东西吗?什么?
  • 噢!对不起。会暂时解决这个问题...

标签: c++ formatmessage fastformat


【解决方案1】:

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++ 格式的作者

【讨论】:

    【解决方案2】:

    由于编译器无法检查要插入格式字符串中的参数数量,因此在编译时不可能做到真正的类型安全。

    您可以通过对不同数量的插入参数进行一些重载,然后使用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
    }
    

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

    【讨论】:

    • 有趣的想法。 FormatMessage() 需要参数的 va_list 或 32 位参数的数组。因此,在第一种情况下,我们需要一个可变参数函数,尽管它可以在您描述的函数内部调用,并且 boost::any 将处理类型安全。或者,我们可以直接构建参数数组。但我对 FastFormat 感兴趣的原因之一是它能够处理任意类型的参数,这意味着可以传递任何类型,然后在调用 FormatMessage() 之前将其转换为 char*。我不认为 boost::any 会允许它
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-12
    • 2012-10-10
    • 1970-01-01
    • 2011-12-18
    • 2014-03-29
    • 1970-01-01
    相关资源
    最近更新 更多