virtualbox-com 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/Gemfile +9 -0
- data/LICENSE +19 -0
- data/Rakefile +8 -0
- data/Readme.md +59 -0
- data/examples/simple.rb +43 -0
- data/lib/virtualbox-com.rb +1 -0
- data/lib/virtualbox/com.rb +51 -0
- data/lib/virtualbox/com/abstract_enum.rb +51 -0
- data/lib/virtualbox/com/abstract_interface.rb +144 -0
- data/lib/virtualbox/com/abstract_model.rb +14 -0
- data/lib/virtualbox/com/exceptions.rb +32 -0
- data/lib/virtualbox/com/iid.rb +43 -0
- data/lib/virtualbox/com/model/4.2-gen.rb +2720 -0
- data/lib/virtualbox/com/model/4.2.rb +97 -0
- data/lib/virtualbox/com/util.rb +119 -0
- data/lib/virtualbox/com/version.rb +5 -0
- data/lib/virtualbox/com/xpcomc-ffi.rb +76 -0
- data/lib/virtualbox/com/xpcomc-ffi/binding.rb +87 -0
- data/lib/virtualbox/com/xpcomc-ffi/implementer.rb +86 -0
- data/lib/virtualbox/com/xpcomc-ffi/lib.rb +90 -0
- data/lib/virtualbox/com/xpcomc-ffi/model-types.rb +15 -0
- data/lib/virtualbox/com/xpcomc-ffi/sig.rb +342 -0
- data/lib/virtualbox/com/xpcomc-ffi/spec.rb +58 -0
- data/lib/virtualbox/com/xpcomc-ffi/xpcomc-vbox.rb +54 -0
- data/scripts/xidl-conv.rb +124 -0
- data/virtualbox-com.gemspec +27 -0
- metadata +123 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'virtualbox/com/util'
|
2
|
+
|
3
|
+
module VirtualBox
|
4
|
+
module COM
|
5
|
+
module XPCOMC
|
6
|
+
|
7
|
+
|
8
|
+
module Lib
|
9
|
+
extend ::FFI::Library
|
10
|
+
|
11
|
+
# Constant with default library path and name
|
12
|
+
# according to running plateform
|
13
|
+
PATH = case Util.platform
|
14
|
+
when :mac
|
15
|
+
[ "/Applications/VirtualBox.app/Contents/MacOS",
|
16
|
+
"/Applications/MacPorts/VirtualBox.app/Contents/MacOS" ]
|
17
|
+
when :linux
|
18
|
+
[ "/opt/VirtualBox",
|
19
|
+
"/usr/lib/virtualbox",
|
20
|
+
"/usr/lib64/virtualbox" ]
|
21
|
+
when :solaris
|
22
|
+
[ "/opt/VirtualBox/amd64",
|
23
|
+
"/opt/VirtualBox/i386" ]
|
24
|
+
when :freebsd
|
25
|
+
[ "/usr/local/lib/virtualbox" ]
|
26
|
+
else
|
27
|
+
[ ]
|
28
|
+
end
|
29
|
+
|
30
|
+
NAME = case Util.platform
|
31
|
+
when :max then "VBoxXPCOMC.dylib"
|
32
|
+
else "VBoxXPCOMC.so"
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def self.init
|
37
|
+
# Only once
|
38
|
+
return if respond_to?(:VBoxGetXPCOMCFunctions)
|
39
|
+
|
40
|
+
# Initialize lib and attach main function
|
41
|
+
ffi_lib_flags(:now, :local)
|
42
|
+
ffi_lib(ENV['VBOX_APP_HOME'] || PATH.map {|path| "#{path}/#{NAME}"})
|
43
|
+
attach_function :VBoxGetXPCOMCFunctions, [:uint], :pointer
|
44
|
+
|
45
|
+
|
46
|
+
# Get the pointer to the XPCOMC struct which contains the functions
|
47
|
+
# to initialize
|
48
|
+
xpcom_ptr = self.VBoxGetXPCOMCFunctions(XPCOMC::VERSION)
|
49
|
+
@@xpcom = XPCOMC::VBox.new(xpcom_ptr)
|
50
|
+
|
51
|
+
|
52
|
+
# Initializes the VirtualBox and Session interfaces.
|
53
|
+
# It goes through the various supported interfaces.
|
54
|
+
SUPPORTED_VERSIONS.each {|version, iids|
|
55
|
+
virtualbox_iid_str, session_iid_str = iids
|
56
|
+
|
57
|
+
# Setup the OUT pointers
|
58
|
+
virtualbox_ptr = ::FFI::MemoryPointer.new(:pointer)
|
59
|
+
session_ptr = ::FFI::MemoryPointer.new(:pointer)
|
60
|
+
|
61
|
+
# Call the initialization functions
|
62
|
+
@@xpcom[:pfnComInitialize].call(virtualbox_iid_str, virtualbox_ptr,
|
63
|
+
session_iid_str, session_ptr)
|
64
|
+
|
65
|
+
# Read the pointers from the results
|
66
|
+
virtualbox_ptr = virtualbox_ptr.read_pointer
|
67
|
+
session_ptr = session_ptr.read_pointer
|
68
|
+
|
69
|
+
# If either pointers are null it means that
|
70
|
+
# the initialization was not successful
|
71
|
+
next if virtualbox_ptr.null? || session_ptr.null?
|
72
|
+
|
73
|
+
# Load the interface description
|
74
|
+
require "virtualbox/com/model/#{version}"
|
75
|
+
|
76
|
+
@@virtualbox = Model.create(:VirtualBox, virtualbox_ptr)
|
77
|
+
@@session = Model.create(:Session, session_ptr)
|
78
|
+
|
79
|
+
break
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.xpcom ; @@xpcom ; end
|
84
|
+
def self.virtualbox ; @@virtualbox ; end
|
85
|
+
def self.session ; @@session ; end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,342 @@
|
|
1
|
+
module VirtualBox
|
2
|
+
module COM
|
3
|
+
module XPCOMC
|
4
|
+
|
5
|
+
class Sig
|
6
|
+
def initialize(sig)
|
7
|
+
@sig = sig.dup.freeze
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
# Converts a function spec from {AbstractInterface} to an FFI
|
12
|
+
# function spec. This handles custom types (unicode strings,
|
13
|
+
# arrays, and out-parameters) and will return a perfectly valid
|
14
|
+
# array ready to be passed into `callback`.
|
15
|
+
#
|
16
|
+
# @param [Array] spec The function spec
|
17
|
+
# @return [Array]
|
18
|
+
def to_ffi
|
19
|
+
spec = @sig.map do |item|
|
20
|
+
if item.is_a?(Array) && item[0] == :out
|
21
|
+
if item[1].is_a?(Array)
|
22
|
+
# The out is an array of items, so we add in
|
23
|
+
# two pointers: one for size and one for the array
|
24
|
+
[ :pointer, :pointer ]
|
25
|
+
else
|
26
|
+
# A regular out parameter is just a single pointer
|
27
|
+
:pointer
|
28
|
+
end
|
29
|
+
elsif item.is_a?(Array) && item.length == 1
|
30
|
+
# The parameter is an array of somethings
|
31
|
+
[ UINT32, :pointer ]
|
32
|
+
elsif item == WSTRING
|
33
|
+
# Unicode strings are simply pointers
|
34
|
+
:pointer
|
35
|
+
elsif item.to_s[0,1] == item.to_s[0,1].upcase
|
36
|
+
# Try to get the class from the interfaces
|
37
|
+
Model.get(item).to_ffi
|
38
|
+
else
|
39
|
+
# Unknown items are simply passed as-is, hopefully FFI
|
40
|
+
# will catch any problems
|
41
|
+
item
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Prepend a :pointer to represent the `this` parameter required
|
46
|
+
# for the FFI parameter lists
|
47
|
+
spec.unshift(:pointer).flatten
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def prepare_args(args=[])
|
52
|
+
args = args.dup
|
53
|
+
|
54
|
+
results = @sig.inject([]) do |results, item|
|
55
|
+
single_type_to_arg(args, item, results)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
# Takes a spec and a formal parameter list and returns the output from
|
63
|
+
# a function, properly dereferencing any output pointers.
|
64
|
+
#
|
65
|
+
# @param [Array] specs The parameter spec for the function
|
66
|
+
# @param [Array] formal The formal parameter list
|
67
|
+
def retrieve_values(formal)
|
68
|
+
return_values = []
|
69
|
+
i = 0
|
70
|
+
@sig.each do |spec|
|
71
|
+
# Output parameters are all we care about
|
72
|
+
if spec.is_a?(Array) && spec[0] == :out
|
73
|
+
if spec[1].is_a?(Array)
|
74
|
+
# We are dealing with formal[i] and formal[i+1]
|
75
|
+
# here, where the first has the size and the
|
76
|
+
# second has the contents
|
77
|
+
return_values << dereference_pointer_array(formal[i+1], spec[1][0], dereference_pointer(formal[i], UINT32))
|
78
|
+
|
79
|
+
# Skip 2: size param + pointer
|
80
|
+
i += 2
|
81
|
+
else
|
82
|
+
return_values << dereference_pointer(formal[i], spec[1])
|
83
|
+
|
84
|
+
# Skip 1: Pointer
|
85
|
+
i += 1
|
86
|
+
end
|
87
|
+
elsif spec.is_a?(Array) && spec.length == 1
|
88
|
+
# This is an array argument, meaning it takes two
|
89
|
+
# arguments: one for size and one is a pointer to the
|
90
|
+
# array items. Therefore, skip two items.
|
91
|
+
i += 2
|
92
|
+
else
|
93
|
+
i += 1
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
if return_values.empty? then nil
|
98
|
+
elsif return_values.length == 1 then return_values.first
|
99
|
+
else return_values
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
# Dereferences a pointer with a given type into a proper Ruby object.
|
117
|
+
# If the type is a standard primitive of Ruby-FFI, it simply calls the
|
118
|
+
# proper `get_*` method on the pointer. Otherwise, it calls a
|
119
|
+
# `read_*` on the Binding class.
|
120
|
+
#
|
121
|
+
# @param [FFI::MemoryPointer] pointer
|
122
|
+
# @param [Symbol] type The type of the pointer
|
123
|
+
# @return [Object] The value of the dereferenced pointer
|
124
|
+
def dereference_pointer(pointer, type)
|
125
|
+
c_type, inferred_type = infer_type(type)
|
126
|
+
method = :"read_#{inferred_type}"
|
127
|
+
|
128
|
+
if pointer.respond_to?(method)
|
129
|
+
result = pointer.send(method)
|
130
|
+
result = result != 0 if type == BOOL
|
131
|
+
result
|
132
|
+
elsif respond_to?(method)
|
133
|
+
send(method, pointer, type)
|
134
|
+
else
|
135
|
+
raise "don't know how to dereference pointer of type #{type}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Dereferences an array out of a pointer into an array of proper Ruby
|
140
|
+
# objects.
|
141
|
+
#
|
142
|
+
# @param [FFI::MemoryPointer] pointer
|
143
|
+
# @param [Symbol] type The type of the pointer
|
144
|
+
# @param [Fixnum] length The length of the array
|
145
|
+
# @return [Array<Object>]
|
146
|
+
def dereference_pointer_array(pointer, type, length)
|
147
|
+
return [] if length == 0
|
148
|
+
|
149
|
+
c_type, inferred_type = infer_type(type)
|
150
|
+
method = :"get_array_of_#{inferred_type}"
|
151
|
+
|
152
|
+
array_pointer = pointer.get_pointer(0)
|
153
|
+
|
154
|
+
if array_pointer.respond_to?(method)
|
155
|
+
array_pointer.send(method, 0, length)
|
156
|
+
elsif respond_to?(method)
|
157
|
+
send(method, array_pointer, type, length)
|
158
|
+
else
|
159
|
+
raise "don't know how to dereference array of type #{type}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
def pointer_for_type(type)
|
166
|
+
c_type, type = infer_type(type)
|
167
|
+
pointer = ::FFI::MemoryPointer.new(c_type)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Gives the C type and inferred type of a parameter type. Quite confusing
|
171
|
+
# since the terminology is not consistent, but hopefully these examples
|
172
|
+
# will help:
|
173
|
+
#
|
174
|
+
# type => [pointer_type, internal_type]
|
175
|
+
# :int => [:int, :int]
|
176
|
+
# :MyStruct => [:pointer, :struct]
|
177
|
+
# :unicode_string => [:pointer, :unicode_string]
|
178
|
+
#
|
179
|
+
def infer_type(type)
|
180
|
+
c_type = type
|
181
|
+
|
182
|
+
begin
|
183
|
+
if type == WSTRING
|
184
|
+
# Handle strings as pointer types
|
185
|
+
c_type = :pointer
|
186
|
+
else
|
187
|
+
# Try to get the class from the interfaces
|
188
|
+
interface = Model.get(type)
|
189
|
+
|
190
|
+
c_type = :pointer
|
191
|
+
|
192
|
+
# Depending on the class type, we're either dealing with an
|
193
|
+
# interface or an enum
|
194
|
+
if interface.superclass == COM::AbstractInterface
|
195
|
+
c_type, type = :pointer, :interface
|
196
|
+
elsif interface.superclass == COM::AbstractEnum
|
197
|
+
c_type, type = :uint32, :enum
|
198
|
+
end
|
199
|
+
end
|
200
|
+
rescue ModelNotFoundException
|
201
|
+
# Do nothing
|
202
|
+
end
|
203
|
+
|
204
|
+
[c_type, type]
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
# Converts a single type and args list to the proper formal args list
|
209
|
+
def single_type_to_arg(args, item, results)
|
210
|
+
if item.is_a?(Array) && item[0] == :out
|
211
|
+
if item[1].is_a?(Array)
|
212
|
+
# For arrays we need two pointers: one for size, and
|
213
|
+
# one for the actual array
|
214
|
+
results << pointer_for_type(UINT32)
|
215
|
+
results << pointer_for_type(item[1][0])
|
216
|
+
else
|
217
|
+
results << pointer_for_type(item[1])
|
218
|
+
end
|
219
|
+
elsif item.is_a?(Array) && item.length == 1
|
220
|
+
# Array argument
|
221
|
+
data = args.shift
|
222
|
+
|
223
|
+
# First add the length of the array
|
224
|
+
results << data.length
|
225
|
+
|
226
|
+
# Create the array
|
227
|
+
c_type, type = infer_type(item.first)
|
228
|
+
|
229
|
+
# If its a regular type (int, bool, etc.) then just make it an
|
230
|
+
# array of that
|
231
|
+
if type != :interface
|
232
|
+
# Build a pointer to an array of values
|
233
|
+
result = ::FFI::MemoryPointer.new(c_type, data.length)
|
234
|
+
adder = result.method("put_#{c_type}")
|
235
|
+
data.each_with_index do |single, index|
|
236
|
+
value = []
|
237
|
+
single_type_to_arg([single], item[0], value)
|
238
|
+
adder.call(index, value.first)
|
239
|
+
end
|
240
|
+
|
241
|
+
results << result
|
242
|
+
else
|
243
|
+
# Then convert the rest into a raw MemoryPointer
|
244
|
+
array = ::FFI::MemoryPointer.new(:pointer, data.length)
|
245
|
+
data.each_with_index do |datum, i|
|
246
|
+
converted = []
|
247
|
+
single_type_to_arg([datum], item.first, converted)
|
248
|
+
array[i].put_pointer(0, converted.first)
|
249
|
+
end
|
250
|
+
|
251
|
+
results << array
|
252
|
+
end
|
253
|
+
elsif item == WSTRING
|
254
|
+
# We have to convert the arg to a unicode string
|
255
|
+
results << XPCOMC::Lib.xpcom.string_to_utf16(args.shift)
|
256
|
+
elsif item == BOOL
|
257
|
+
results << (args.shift ? 1 : 0)
|
258
|
+
elsif item.to_s[0,1] == item.to_s[0,1].upcase
|
259
|
+
# Try to get the class from the interfaces
|
260
|
+
interface = Model.get(item)
|
261
|
+
val = args.shift
|
262
|
+
|
263
|
+
results << if interface.superclass == COM::AbstractInterface
|
264
|
+
# For interfaces, get the instance, then
|
265
|
+
# dig deep to get the pointer to the
|
266
|
+
# VtblParent, which is what the API expects
|
267
|
+
val && val.implementer.binding.object
|
268
|
+
elsif interface.superclass == COM::AbstractEnum
|
269
|
+
# For enums, we need the value of the enum
|
270
|
+
interface.index(val)
|
271
|
+
end
|
272
|
+
else
|
273
|
+
# Simply replace spec item with next item in args
|
274
|
+
# list
|
275
|
+
results << args.shift
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
|
281
|
+
|
282
|
+
|
283
|
+
|
284
|
+
|
285
|
+
# Reads a unicode string value from a pointer to that value.
|
286
|
+
#
|
287
|
+
# @return [String]
|
288
|
+
def read_unicode_string(ptr, original_type=nil)
|
289
|
+
XPCOMC::Lib.xpcom.utf16_to_string(ptr.read_pointer) || ''
|
290
|
+
end
|
291
|
+
|
292
|
+
# Reads an interface from the pointer
|
293
|
+
#
|
294
|
+
# @return [::FFI::Struct]
|
295
|
+
def read_interface(ptr, original_type)
|
296
|
+
ptr = ptr.read_pointer
|
297
|
+
return nil if ptr.null?
|
298
|
+
Model.create(original_type, ptr)
|
299
|
+
end
|
300
|
+
|
301
|
+
# Reads an enum
|
302
|
+
#
|
303
|
+
# @return [Symbol]
|
304
|
+
def read_enum(ptr, original_type)
|
305
|
+
Model.get(original_type)[ptr.get_uint(0)]
|
306
|
+
end
|
307
|
+
|
308
|
+
# Reads an array of enums
|
309
|
+
#
|
310
|
+
# @return [Array<Symbol>]
|
311
|
+
def get_array_of_enum(ptr, type, length)
|
312
|
+
klass = Model.get(type)
|
313
|
+
ptr.get_array_of_uint(0, length).map do |value|
|
314
|
+
klass[value]
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
# Reads an array of structs from a pointer
|
319
|
+
#
|
320
|
+
# @return [Array<::FFI::Struct>]
|
321
|
+
def get_array_of_interface(ptr, type, length)
|
322
|
+
klass = Model.get(type)
|
323
|
+
ptr.get_array_of_pointer(0, length).map do |pointer|
|
324
|
+
klass.new(pointer)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
# Reads an array of strings from a pointer
|
329
|
+
#
|
330
|
+
# @return [Array<String>]
|
331
|
+
def get_array_of_unicode_string(ptr, type, length)
|
332
|
+
ptr.get_array_of_pointer(0, length).map do |pointer|
|
333
|
+
XPCOMC::Lib.xpcom.utf16_to_string(pointer)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
|
338
|
+
end
|
339
|
+
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative 'sig'
|
2
|
+
|
3
|
+
module VirtualBox
|
4
|
+
module COM
|
5
|
+
module XPCOMC
|
6
|
+
|
7
|
+
class Spec
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
def hide?
|
11
|
+
@opts[:hide]
|
12
|
+
end
|
13
|
+
|
14
|
+
class Function < Spec
|
15
|
+
def initialize(name, type, args, opts)
|
16
|
+
@name, @type, @args, @opts = name, type, args, opts
|
17
|
+
end
|
18
|
+
|
19
|
+
def signatures
|
20
|
+
{ name => to_call }
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_call
|
24
|
+
Sig.new( if @type.nil?
|
25
|
+
then @args
|
26
|
+
else @args + [ [ :out, @type ] ]
|
27
|
+
end)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Property < Spec
|
32
|
+
attr_reader :getter, :setter
|
33
|
+
|
34
|
+
def initialize(name, type, opts)
|
35
|
+
@name, @type, @opts = name, type, opts
|
36
|
+
end
|
37
|
+
|
38
|
+
def signatures
|
39
|
+
r = {}
|
40
|
+
r[getter] = to_read
|
41
|
+
r[setter] = to_write unless self.readonly?
|
42
|
+
r
|
43
|
+
end
|
44
|
+
|
45
|
+
def readonly?
|
46
|
+
@opts[:readonly]
|
47
|
+
end
|
48
|
+
|
49
|
+
def getter ; :"get_#{@name}" ; end
|
50
|
+
def setter ; :"set_#{@name}" ; end
|
51
|
+
def to_read ; Sig.new([[:out, @type]]) ; end
|
52
|
+
def to_write ; Sig.new([ @type ]) ; end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|