[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