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