Daniel Berger
2008-Jan-26 17:40 UTC
[Win32utils-devel] JRuby version of win32-api - initial try
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
