What's a correct way to call FormatMessageW in Ruby?
require 'win32api'
FormatMessage = Win32API.new 'kernel32', 'FormatMessageW', 'IPIIPII', 'I'
msg = '\0' * 255
FormatMessage.call 0x00001000 | 0x00000100, nil, 6, 1024, msg, 0, 0
FormatMessage returns not null result but msg contains not readable message. What's wrong?
I believe this is the code you are looking for:
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
When I run this with Ruby, the output is "The handle is invalid.", which is the correct Windows error message for error code 6.
There were some problems with your original code.
P
instead of I
, especially if you want it to work on 64-bit Windows.'\0'
is actually a two-byte ASCII string, not a single-byte null character. You can confirm this by running p '\0'.bytes.to_a
. It looks like you tried to allocate 255 bytes, but you actually allocated 510 bytes. You should allocate an even number of bytes because wide characters in Windows take 2 bytes.FormatMessageW
was wrong, since you specified that FormatMessageW
should allocate its own buffer.force_encoding
to set the encoding of your string to UTF-16LE, because that is the encoding used for wide strings in Windows (or if it's not exactly the same, at least it is compatible most of the time).String#size
will return surprising results. FormatMessageW
returns the number of characters in the formatted message, so we can use that to trim off the null characters at the end. (Conveniently, FormatMessageW
returns 0 if there is an error, so our trimming would result in an empty string.)String#encode
to convert your string from UTF-16LE to UTF-8 because UTF-8 strings are much easier to operate on and print in Ruby.If you don't care about internationalization and unicode, you could have just used FormatMessageA
instead. Here is some code that will work for that:
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 is an unsigned integer type. I am not sure what the right letter for that is in Ruby's Win32API class; it might be that I
represents a signed integer, and should be replaced by something else.
User contributions licensed under CC BY-SA 3.0