Ruby:FormatMessageW有问题

时间:2015-10-02 16:19:08

标签: ruby winapi

在Ruby中调用FormatMessageW的正确方法是什么?

   require 'win32api'

   FormatMessage = Win32API.new 'kernel32', 'FormatMessageW', 'IPIIPII', 'I'
   msg = '\0' * 255
   FormatMessage.call 0x00001000 | 0x00000100, nil, 6, 1024, msg, 0, 0

FormatMessage返回非null结果,但msg包含不可读消息。怎么了?

1 个答案:

答案 0 :(得分:0)

我相信这是您正在寻找的代码:

require 'win32api'

FORMAT_MESSAGE_FROM_SYSTEM = 0x1000
FormatMessage = Win32API.new 'kernel32', 'FormatMessageW', 'IPIIPIP', 'I'
msgw = ("\x00" * 256).force_encoding("UTF-16LE")
count = FormatMessage.call FORMAT_MESSAGE_FROM_SYSTEM, nil, 6, 1033, msgw, msgw.size, nil
msgw = msgw[0, count]
msg = msgw.encode("UTF-8")
puts msg

当我使用Ruby运行时,输出为"句柄无效。",这是错误代码6的正确Windows错误消息。

您的原始代码存在一些问题。

  1. FormatMessageW的最后一个参数是一个指针,因此您应该使用P而不是I,尤其是如果您希望它在64位Windows上运行。
  2. 在Ruby中'\0'实际上是一个双字节的ASCII字符串,而不是单字节的空字符。您可以通过运行p '\0'.bytes.to_a来确认这一点。看起来你试图分配255个字节,但实际上你分配了510个字节。您应该分配偶数个字节,因为Windows中的宽字符占用2个字节。
  3. 正如@theB指出的那样,你FormatMessageW的第一个参数是错误的,因为你指定FormatMessageW应该分配自己的缓冲区。
  4. 您指定了语言代码1024.我无法找到该定义。也许你的意思是1033,这是"英语 - 美国"。指定1024似乎实际上不会导致问题。
  5. 您应该使用force_encoding将字符串的编码设置为UTF-16LE,因为这是用于Windows中宽字符串的编码(或者如果它不完全相同,至少它大部分时间是兼容的。)
  6. FormatMessageW的第6个参数应该是缓冲区中的字符数(顺便说一下,字节数除以2)。您的代码刚刚为该参数传递了0。
  7. Ruby中的字符串可以包含任意字节,包括空字符,但这样做不一定是个好主意,因为String#size之类的内容会返回令人惊讶的结果。 FormatMessageW返回格式化消息中的字符数,因此我们可以使用它来删除末尾的空字符。 (方便的是,FormatMessageW如果有错误则返回0,因此我们的修剪会导致空字符串。)
  8. 您应该使用String#encode将字符串从UTF-16LE转换为UTF-8,因为UTF-8字符串更容易操作并在Ruby中打印。
  9. 如果您不关心国际化和unicode,您可以使用FormatMessageA代替。以下是一些适用于此的代码:

    require 'win32api'
    
    FORMAT_MESSAGE_FROM_SYSTEM = 0x1000
    FormatMessage = Win32API.new 'kernel32', 'FormatMessageA', 'IPIIPIP', 'I'
    msg = ("\x00" * 256).force_encoding("ASCII-8BIT")
    count = FormatMessage.call FORMAT_MESSAGE_FROM_SYSTEM, nil, 6, 1033, msg, msg.size, nil
    msg = msg[0, count]
    puts msg
    

    P.S。 DWORD是无符号整数类型。我不确定Ruby的Win32API类中有哪些正确的字母;可能是I表示有符号整数,应该用其他东西替换。