Ruby and Unicode Win32 MessageBoxes

Have you ever needed to display Unicode characters in a Win32 MessageBox from a Ruby script? My pair and I needed to do just that and so I thought I would share what we found.

There are a number of ways to access Win32 calls from a Ruby script. The code we were working with did the following:

1
2
3
4
5
6
7
8
9
require 'dl'
def show_message_box(message, title)
  mb_ok = 0
  mb_iconexclamation = 48
  
  user32 = DL.dlopen("user32")
  message_box = user32['MessageBoxA', 'ILSSI']
  message_box.call(0, message, title, mb_ok | mb_iconexclamation)
end

That works well until you need to display Unicode characters in the MessageBox. It turns out the MessageBoxA version of the function is for ASCII characters. There is another version of the API call, MessageBoxW, that can handle Unicode, or wide characters. So the issue becomes converting your Ruby string into a wide string so it can be passed to MessageBoxW. The MultiByteToWideChar Win32 call can do this for you. And the windows-pr gem (from win32utils) adds a nice ruby wrapper around the function.


gem install windows-pr
1
2
3
4
5
require 'windows/unicode' 
include Windows::Unicode 
$KCODE='UTF8'

str = multi_to_wide("This is a test")

This seemed to work quite well for the most part. However, from time to time we would see garbage text showing up at the end of our messages. It could easily be reproduced if the message was very short.

Having done enough C/C++ coding to recognize a string that was not being null terminated, we experimented with adding null characters to the end of the string. It turns out a wide null terminator (”\0\0”) is needed. The following code will properly display Unicode characters in a Win32 MessageBox:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require 'dl'
require 'windows/unicode' 
include Windows::Unicode 
$KCODE='UTF8'

def win32_wide(text)
  multi_to_wide(text) + "\0\0"
end

def show_message_box(message, title)
  mb_ok = 0
  mb_iconexclamation = 48
  
  user32 = DL.dlopen("user32")
  message_box = user32['MessageBoxW', 'ILSSI']
  message_box.call(0, win32_wide(message), win32_wide(title), 
                             mb_ok | mb_iconexclamation)
end

show_message_box "MessageBox displayed from Ρουμπίνι", "¡Alert!"
Filed in: Languages, Tips, Tools


Leave a Reply