Help with Win32API, LoadLibrary and UTF-8 paths

#1

I’m having trouble passing UTF-8 paths to the Win32API. I am using LoadLibrary to load a DLL using Ruby. This is what I have:

loadLibrary = Win32API.new(“kernel32”, “LoadLibrary”, [‘P’], ‘L’)
result = loadLibrary.Call(dest_path)
if result == 0
UI.messagebox(LH[“Failed to load dll from #{dest_path}”])
raise “Failed to load DLL”
end

My problem is that if dest_path includes multi-byte UTF-8 characters, the call to LoadLibrary will fail.

I have also tried to solve this by using LoadLibraryW, which I believe is for handling wide multi-byte character paths but have not had success yet.

I have also tried to force the Ruby encoding of ‘dest_path’ to Encoding::Windows_1252 and Encoding::UTF_16 but it did not work. Perhaps I did something wrong or am missing a step.

Any advice would be greatly appreciated! Thanks!

#2

If I’m not mistaken, “forcing” an encoding just changes the encoding metadata for the string without doing any actual conversion.

EDIT: Maybe this article is helpful https://www.justinweiss.com/articles/3-steps-to-fix-encoding-problems-in-ruby/.

#3

I’ve also done the actual encoding conversion using string.encode. Still doesn’t work

#4

utf-16le instead of utf-16?

Maybe have a look at:

Ruby has two main test suites, the larger of which is the ‘test-all’ suite. Current file location is at https://github.com/ruby/ruby/tree/trunk/test.

#5

Win32API is a rather old module in Ruby. My impression is that Fiddle is better maintained. (And it works on mac and linux - in terms of calling libraries)

https://ruby-doc.org/stdlib-2.5.3/libdoc/fiddle/rdoc/Fiddle.html

Here is an example of Fiddle calling both the A and W version of MessageBox:

# Load importer part of fiddle (ffi) library
require 'fiddle/import'

# Create module as body for an importer instance
module MessageBox
	# Extend this module to an importer
	extend Fiddle::Importer
	# Load 'user32' dynamic library into this importer
	dlload 'user32'
	# Set C aliases to this importer for further understanding of function signatures
	typealias 'HANDLE', 'void*'
	typealias 'HWND', 'HANDLE'
	typealias 'LPCSTR', 'const char*'
	typealias 'LPCWSTR', 'const wchar_t*'
	typealias 'UINT', 'unsigned int'
	# Import C functions from loaded libraries and set them as module functions
	extern 'int MessageBoxA(HWND, LPCSTR, LPCSTR, UINT)'
	extern 'int MessageBoxW(HWND, LPCWSTR, LPCWSTR, UINT)'
end

# Tests
MessageBox::MessageBoxA nil, "Hello world!の", "Fiddle ANSI", 0
MessageBox::MessageBoxW nil, "Hello world!の".encode("utf-16le"), "Fiddle WIDECHAR".encode("utf-16le"), 0
#6

Thanks so much, Greg! It seems to be working now if I first encode the path using utf-16le and use LoadLibraryW.

dest_path = dest_path.encode(“utf-16le”)
loadLibrary = Win32API.new(“kernel32”, “LoadLibraryW”, [‘P’], ‘L’)
result = loadLibrary.Call(dest_path)

#7

@tt_su

Thanks! I didn’t get a chance to try Fiddle since Greg offered a simpler solution that seems to have worked.

But, this is great info to know for the future. Much appreciated!

#8

Glad that helped. I was about 99% sure that was the issue, but I didn’t test it.

@tt_su mentioned that Win32API is old (and sort of deprecated), but the most recent Ruby stdlib uses it. For Windows, it works, will probably continue to work, etc…

1 Like