Module: WinWindow::AttachLib

Included in:
WinGDI, WinKernel, WinUser
Defined in:
lib/winwindow.rb

Overview

this module exists because I’ve implemented this library for DL, for FFI, and for Win32::API. Getting tired of changing everything everywhere, now it just takes changes to Types, and a few methods (use_lib, attach, callback) to switch to another library.

Constant Summary collapse

IsWin64 =

:nodoc: all

nil
Types =

types that FFI recognizes

[:char, :uchar, :int, :uint, :short, :ushort, :long, :ulong, :void, :pointer, :string].inject({}) do |type_hash, type|
  type_hash[type]=type
  type_hash
end

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.add_type(hash) ⇒ Object

# here begins the Win32::API version. this one doesn’t work because of a hard-coded limit on

# callbacks in win32/api.c combined with a lack of any capacity to remove any callbacks. 
require 'win32/api'

# basic types that Win32::API recognizes
Types={ :char => 'I', # no 8-bit type in Win32::API?
        :uchar => 'I', # no unsigned types in Win32::API?
        :int => 'I',
        :uint => 'I',
        :long => 'L',
        :ulong => 'L',
        :void => 'V',
        :pointer => 'P',
        :callback => 'K',
        :string => 'P', # 'S' works here on mingw32, but not on mswin32 
      }

def use_lib(lib)
  @lib=lib
end
# this takes arguments in the order that they're given in c/c++ so that signatures look kind of like the source
def attach(return_type, function_name, *arg_types)
  the_function=Win32::API.new(function_name.to_s, arg_types.map{|arg_type| Types[arg_type] }.join(''), Types[return_type], @lib)
  metaclass=class << self;self;end
  metaclass.send(:define_method, function_name) do |*args|
    the_function.call(*args)
  end
  nil
end
# this takes arguments like #attach, but with a name for the callback's type on the front. 
def callback(callback_type_name, return_type, callback_method_name, *arg_types)
  Types[callback_type_name]=Types[:callback]

  # perform some hideous class_eval'ing to dynamically define the callback method such that it will take a block 
  metaclass=class << self;self;end
  metaclass.class_eval("def #{callback_method_name}(&block)
    #{callback_method_name}_with_arg_stuff_in_scope(block)
  end")
  types=Types
  metaclass.send(:define_method, callback_method_name.to_s+"_with_arg_stuff_in_scope") do |block|
    return Win32::API::Callback.new(arg_types.map{|t| types[t]}.join(''), types[return_type], &block)
  end
  def remove_#{callback_method_name}(callback_method)
    # Win32::API has no support for removing callbacks? 
    nil
  end")
  # don't use define_method as this will be called from an ensure block which segfaults ruby 1.9.1. see http://redmine.ruby-lang.org/issues/show/2728
  #metaclass.send(:define_method, "remove_"+callback_method_name.to_s) do |callback_method|
  #  nil
  #end
  nil
end


187
188
189
190
191
192
193
194
# File 'lib/winwindow.rb', line 187

def self.add_type(hash)
  hash.each_pair do |key, value|
    unless Types.key?(value)
      raise "unrecognized type #{value.inspect}"
    end
    Types[key]=Types[value]
  end
end

.extended(extender) ⇒ Object



70
71
72
73
74
# File 'lib/winwindow.rb', line 70

def self.extended(extender)
  ffi_module=Module.new
  ffi_module.send(:extend, FFI::Library)
  extender.send(:instance_variable_set, '@ffi_module', ffi_module)
end

Instance Method Details

#attach(return_type, function_name, *arg_types) ⇒ Object

this takes arguments in the order that they’re given in c/c++ so that signatures look kind of like the source



80
81
82
83
84
85
86
87
88
# File 'lib/winwindow.rb', line 80

def attach(return_type, function_name, *arg_types)
  @ffi_module.attach_function(function_name, arg_types.map{|arg_type| Types[arg_type] }, Types[return_type])
  metaclass=class << self;self;end
  ffi_module=@ffi_module
  metaclass.send(:define_method, function_name) do |*args|
    ffi_module.send(function_name, *args)
  end
  nil
end

#callback(callback_type_name, return_type, callback_method_name, *arg_types) ⇒ Object

this takes arguments like #attach, but with a name for the callback’s type on the front.



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/winwindow.rb', line 90

def callback(callback_type_name, return_type, callback_method_name, *arg_types)
  
  Types[callback_type_name]=callback_type_name
  
  #@ffi_module.callback(callback_type_name, arg_types.map{|type| Types[type]}, Types[return_type])
  
  # we do not call @ffi_module.callback here, because it is broken. we need to pass the convention ourselves in the options hash. 
  # this is adapted from: http://gist.github.com/256660
  options={}
  types=Types
  @ffi_module.instance_eval do
    options[:convention] = defined?(@ffi_convention) ? @ffi_convention : :default
    options[:enums] = @ffi_enums if defined?(@ffi_enums)

    cb = FFI::CallbackInfo.new(find_type(types[return_type]), arg_types.map{|e| find_type(types[e]) }, options)

    @ffi_callbacks = Hash.new unless defined?(@ffi_callbacks)
    @ffi_callbacks[callback_type_name] = cb
  end
  #options[:convention] = @ffi_module.instance_variable_defined?('@ffi_convention') ? @ffi_module.instance_variable_get('@ffi_convention') : :default
  #options[:enums] = @ffi_module.instance_variable_get('@ffi_enums') if @ffi_module.instance_variable_defined?('@ffi_enums')
  #unless @ffi_module.instance_variable_defined?('@ffi_callbacks')
  #  @ffi_module.instance_variable_set('@ffi_callbacks', cb)
  #end
  
  # perform some hideous class_eval'ing to dynamically define the callback method such that it will take a block 
  metaclass=class << self;self;end

  # FFI just takes the block itself. don't need anything fancy here. 
  metaclass.class_eval("def #{callback_method_name}(&block)
    block
  end
  def remove_#{callback_method_name}(callback_method)
    # FFI has no support for removing callbacks? 
    nil
  end")
  # don't use define_method as this will be called from an ensure block which segfaults ruby 1.9.1. see http://redmine.ruby-lang.org/issues/show/2728
  #metaclass.send(:define_method, "remove_"+callback_method_name.to_s) do |callback_method|
  #  nil
  #end
  nil
end

#use_lib(lib) ⇒ Object



75
76
77
78
# File 'lib/winwindow.rb', line 75

def use_lib(lib)
  @ffi_module.ffi_lib lib
  @ffi_module.ffi_convention :stdcall
end