JRuby meets the Windows API
Date : 01 25 2008 Category : WebWith the addition of Java Native Access (JNA) to JRuby, systems programmers using JRuby now have greater flexibility in terms of interfacing with underlying operating system.
Some Ruby users are familiar with the ‘Win32API’ library that ships as part of the Ruby standard library. That library lets you interface with the Windows API by defining function pointers from specific DLL’s that you later call. With JRuby’s JNA interface you can now interface with Windows in a similar fashion.
Getting StartedThe first thing we need to do is require the ‘java’ library.
require 'java'With that out of the way, we’ll then want to get an instance of the DLL we need to export functions from. I’ll use Kernel32 for our purposes.
Kernel32 = com.sun.jna.NativeLibrary.getInstance('kernel32')If you’re familiar with the Windows API, we’ve effectively called the LoadLibrary() function which maps the kernel32 Windows module into our address space, wrapped by our ‘Kernel32′ variable. From here we can define any functions that the kernel32.dll exports. For demonstration purposes I’ll define the GetCurrentDirectoryA() function:
GetCurrentDirectoryA = Kernel32.getFunction('GetCurrentDirectoryA')This effectively calls the Windows GetProcAddress() function behind the scenes, returning a function pointer address, wrapped in our ‘GetCurrentDirectoryA’ variable.
The Nitty GrittyNow that we have a function pointer, we want to call it. This is trickier than the standard Win32API library for two reasons. First, you must know the return type of the GetCurrentDirectoryA() function in order to know how to properly invoke it. I know from looking at the documentation that it returns a long (DWORD). Looking at the JNA documentation1 we can see there’s an invokeLong() function. Let’s try it.
buf = 0.chr * 256 num = GetCurrentDirectory.invokeLong(buf.length, buf) puts buf.stripOops! That won’t work because in Java strings are immutable. We have to convert our Ruby buffer to a ByteBuffer first, then convert it back to a regular Ruby string once we’re ready using the String#from_java_bytes method. Also, the invokeLong() function takes a Java array. We need to explicitly coerce the Ruby array using the .to_java method.2 Armed with this knowledge, let’s try again:
buf = java.nio.ByteBuffer.allocate(256) num = GetCurrentDirectory.invokeLong([256, buf].to_java) buf = String.from_java_bytes(buf.array) puts buf.stripThat will return a string like ‘C:Documents and Settingssome_user’, or whatever your current directory happens to be.
That’s all for now folks. In the future I plan to publish a JRuby gem of the win32-api library. Stay tuned.3
1The JNA home page is at https://jna.dev.java.net/
2You can find more information at http://www.coffee-bytes.com/node/558
3I have a preliminary version in CVS. You can find the win32-api library at http://www.rubyforge.org/projects/win32utils.