[Win32utils-devel] JRuby version of win32-api - initial try
Daniel Berger
djberg96 at gmail.com
Sat Jan 26 12:40:44 EST 2008
Hi all,
I've got a preliminary JRuby version of win32-api checked into CVS at
the moment under win32-api/lib/win32/api.rb. I've also pasted it below.
It doesn't quite work right in that I can't get it to modify the
arguments in place in the API#call method. Take a look at the sample
code at the very bottom to see what I mean.
I haven't even tried adding callback support yet.
If anyone is interested in taking a stab at it I would greatly
appreciate it!
Regards,
Dan
require 'java'
# The Win32 module serves as a namespace only.
module Win32
class API
class Error < StandardError; end
private
KERNEL32 = com.sun.jna.NativeLibrary.getInstance('kernel32')
LoadLibrary = KERNEL32.getFunction('LoadLibraryA')
GetProcAddress = KERNEL32.getFunction('GetProcAddress')
FormatMessageA = KERNEL32.getFunction('FormatMessageA')
LocalFree = KERNEL32.getFunction('LocalFree')
public
VERSION = '1.0.6'
attr_reader :function_name
attr_reader :prototype
attr_reader :return_type
attr_reader :dll_name
def initialize(function, prototype='V', return_type='L',
dll='kernel32')
# Convert a prototype string to an array of characters
if prototype.respond_to?(:split)
prototype = prototype.split('')
end
# Set an arbitrary limit of 16 parameters
if prototype.length > 16
raise ArgumentError, "too many parameters: " + prototype.length
end
dll.downcase!
prototype = 'V' if prototype.nil? || prototype.empty?
return_type = 'L' if return_type.nil? || return_type.empty?
dll = 'kernel32' if dll.nil? || dll.empty?
prototype.each do |proto|
unless ['I', 'L', 'P','B','K'].include?(proto)
raise ArgumentError, "illegal prototype '#{proto}'"
end
end
@function_name = function
@prototype = prototype
@return_type = return_type
@dll_name = dll
if dll == 'kernel32'
@dll = KERNEL32
else
@dll = com.sun.jna.NativeLibrary.getInstance(@dll_name)
end
@library = LoadLibrary.invokeInt([@dll_name].to_java)
if @library.nil? || @library == 0
raise Error, "LoadLibrary() function failed for '#{@dll_name}'"
end
@func = GetProcAddress.invokeInt([@library,
@function_name].to_java)
if $KCODE == 'UTF8'
first, last = 'W', 'A'
else
first, last = 'A', 'W'
end
# Try the original function, then the ANSI version, then Wide
version.
# Try the Wide version first if $KCODE is set to UTF8.
#--
# Unlike the C version we won't be using the FARPROC. This is
mostly
# for validation because the JNA function can't be rescued
directly.
if @func == 0
@function_name += first
@func = GetProcAddress.invokeInt([@library,
@function_name].to_java)
if @func == 0
@function_name[-1,1] = last
@func = GetProcAddress.invokeInt([@library,
@function_name].to_java)
if @func == 0
raise Error, 'GetProcAddress failed'
end
end
end
# Now, turn @func into a Java::ComSunJna::Function object that
we can
# call later
@func = @dll.getFunction(@function_name)
end
def call(*args)
params = []
# For void prototypes, allow either no args or an explicit nil.
# Otherwise, if the prototype length and the argument length don't
# match, raise an error.
if @prototype.length != args.length
if @prototype[0] == 'V'
args = [nil]
else
err = "wrong number of parameters: expected "
err += "#{@prototype.length}, got #{args.length}"
raise ArgumentError, err
end
end
# Consider any string that starts with a "\0" to be a byte buffer
# and convert it automatically. This is necessary because Java
# strings are immutable.
args.each_with_index do |arg, i|
if arg.is_a?(String) && arg[0,1] == "\0"
args[i, 1] = java.nio.ByteBuffer.allocate(arg.length)
end
end
args = [*args].to_java
# Call the proper JNA invocation based on the return type
case @return_type
when 'P'
rv = @func.invokePointer(args)
when 'L'
rv = @func.invokeLong(args)
when 'I', 'B'
rv = @func.invokeInt(args)
when 'V'
rv = nil
else
rv = 0
end
args = args.map{ |e|
if e.respond_to?(:array)
e = String.from_java_bytes(e.array)
else
e
end
}
rv
end
end
end
if $0 == __FILE__
include Win32
GetCurrentDirectoryA = API.new('GetCurrentDirectoryA', 'LP', 'L',
'kernel32')
buf = 0.chr * 70
p GetCurrentDirectoryA.call(buf.length, buf)
puts buf # => NOT WORKING
end
More information about the win32utils-devel
mailing list