win32ole-ffi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/BSDL +22 -0
- data/CHANGELOG.md +10 -0
- data/COPYING +56 -0
- data/README.md +49 -0
- data/lib/win32ole/ffi/array.rb +312 -0
- data/lib/win32ole/ffi/dispatch.rb +125 -0
- data/lib/win32ole/ffi/event.rb +800 -0
- data/lib/win32ole/ffi/fiddle_compat.rb +49 -0
- data/lib/win32ole/ffi/method.rb +237 -0
- data/lib/win32ole/ffi/param.rb +111 -0
- data/lib/win32ole/ffi/record.rb +289 -0
- data/lib/win32ole/ffi/type.rb +337 -0
- data/lib/win32ole/ffi/typeinfo.rb +488 -0
- data/lib/win32ole/ffi/typelib.rb +384 -0
- data/lib/win32ole/ffi/variable.rb +117 -0
- data/lib/win32ole/ffi/variant.rb +351 -0
- data/lib/win32ole/ffi/version.rb +5 -0
- data/lib/win32ole/ffi/win32.rb +907 -0
- data/lib/win32ole/ffi/win32ole.rb +827 -0
- data/lib/win32ole/ffi.rb +48 -0
- data/sig/win32ole/ffi.rbs +308 -0
- metadata +84 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'win32ole/ffi/win32'
|
|
4
|
+
require 'win32ole/ffi/typeinfo'
|
|
5
|
+
require 'win32ole/ffi/type'
|
|
6
|
+
|
|
7
|
+
class WIN32OLE
|
|
8
|
+
class TypeLib
|
|
9
|
+
W = Win32
|
|
10
|
+
TI = TypeInfo
|
|
11
|
+
private_constant :W, :TI
|
|
12
|
+
|
|
13
|
+
LIBFLAG_FRESTRICTED = 0x1
|
|
14
|
+
LIBFLAG_FHIDDEN = 0x4
|
|
15
|
+
|
|
16
|
+
# Mirrors ext/win32ole/win32ole_typelib.c's foletypelib_initialize:
|
|
17
|
+
# +typelib+ is tried, in order, as a registered typelib name
|
|
18
|
+
# (major/minor ignored if this matches), then as a typelib GUID
|
|
19
|
+
# (major/minor pick a specific version, else the highest registered),
|
|
20
|
+
# then as a literal file path.
|
|
21
|
+
def self.new(*args)
|
|
22
|
+
unless (1..3).cover?(args.size)
|
|
23
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1..3)"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
typelib = check_string!(args[0])
|
|
27
|
+
file = file_from_name(typelib) || file_from_guid_and_version(typelib, args[1], args[2])
|
|
28
|
+
itypelib_ptr = load_typelib(file || typelib)
|
|
29
|
+
raise WIN32OLE::RuntimeError, "not found type library `#{typelib}`" if itypelib_ptr.nil?
|
|
30
|
+
|
|
31
|
+
allocate.tap { |tlib| tlib.send(:initialize, itypelib_ptr) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.check_string!(val)
|
|
35
|
+
return val if val.is_a?(::String)
|
|
36
|
+
return val.to_str if val.respond_to?(:to_str)
|
|
37
|
+
|
|
38
|
+
raise TypeError, "no implicit conversion of #{val.class} into String"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.load_typelib(file)
|
|
42
|
+
out = ("\x00" * W::PTR_SIZE).b
|
|
43
|
+
hr = TI.load_type_lib_ex.call(W.wstr(file), TI::REGKIND_NONE, out)
|
|
44
|
+
return nil if W.failed?(hr)
|
|
45
|
+
|
|
46
|
+
out.unpack1(W::PACK_PTR)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Mirrors oletypelib_search_registry2: +guid+ is a typelib GUID
|
|
50
|
+
# string (TypeLib\{guid} directly, not the CLSID\{clsid} root
|
|
51
|
+
# file_from_clsid uses). If major is given, only that exact version
|
|
52
|
+
# is accepted; otherwise the highest version with a registered name
|
|
53
|
+
# wins, same tie-break as file_from_name_under_guid.
|
|
54
|
+
def self.file_from_guid_and_version(guid, major, minor)
|
|
55
|
+
with_reg_subkey(TI::HKEY_CLASSES_ROOT, "TypeLib\\#{guid}") do |hguid|
|
|
56
|
+
version = version_string(major, minor)
|
|
57
|
+
if version
|
|
58
|
+
valid = with_reg_subkey(hguid, version) { |hversion| !reg_default_string_value(hversion).nil? }
|
|
59
|
+
version = nil unless valid
|
|
60
|
+
else
|
|
61
|
+
version = highest_named_version_under(hguid)
|
|
62
|
+
end
|
|
63
|
+
version && file_under_version_key(hguid, version)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.version_string(major, minor)
|
|
68
|
+
return nil if major.nil?
|
|
69
|
+
|
|
70
|
+
minor.nil? ? major.to_s : "#{major}.#{minor}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.highest_named_version_under(hguid)
|
|
74
|
+
best_version = nil
|
|
75
|
+
best_fver = nil
|
|
76
|
+
i = 0
|
|
77
|
+
while (version = TI.reg_enum_key(hguid, i))
|
|
78
|
+
i += 1
|
|
79
|
+
fver = version.to_f
|
|
80
|
+
next if best_fver && fver <= best_fver
|
|
81
|
+
|
|
82
|
+
valid = with_reg_subkey(hguid, version) { |hversion| !reg_default_string_value(hversion).nil? }
|
|
83
|
+
if valid
|
|
84
|
+
best_fver = fver
|
|
85
|
+
best_version = version
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
best_version
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.from_itypelib_ptr(itypelib_ptr)
|
|
92
|
+
allocate.tap { |tlib| tlib.send(:initialize, itypelib_ptr) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def initialize(itypelib_ptr)
|
|
96
|
+
# As the C allocator does, so the finalizer's ole_initialized? check
|
|
97
|
+
# holds on the thread that made this object.
|
|
98
|
+
WIN32OLE.ole_initialize
|
|
99
|
+
@ptr = itypelib_ptr
|
|
100
|
+
# Install the finalizer before any call that could raise: the caller
|
|
101
|
+
# has already AddRef'd this pointer, so if GetLibAttr/read_documentation
|
|
102
|
+
# (or the vtable lookups they perform) raise, this Release must still
|
|
103
|
+
# happen -- otherwise the reference leaks permanently.
|
|
104
|
+
install_finalizer
|
|
105
|
+
|
|
106
|
+
lib_attr_out = ("\x00" * W::PTR_SIZE).b
|
|
107
|
+
hr = TI.lib_attr_fn(@ptr).call(@ptr, lib_attr_out)
|
|
108
|
+
if W.failed?(hr)
|
|
109
|
+
raise WIN32OLE::QueryInterfaceError, W.query_interface_error_message('GetLibAttr', W.hr_hex(hr))
|
|
110
|
+
end
|
|
111
|
+
attr_ptr = lib_attr_out.unpack1(W::PACK_PTR)
|
|
112
|
+
p = Fiddle::Pointer.new(attr_ptr)
|
|
113
|
+
@guid_bytes = p[0, 16]
|
|
114
|
+
@lcid = p[16, 4].unpack1('L')
|
|
115
|
+
@major = p[16 + 4 + 4, 2].unpack1('S') # skip lcid(4) + syskind(4, enum-sized)
|
|
116
|
+
@minor = p[16 + 4 + 4 + 2, 2].unpack1('S')
|
|
117
|
+
@lib_flags = p[16 + 4 + 4 + 2 + 2, 2].unpack1('S')
|
|
118
|
+
TI.release_tlib_attr_fn(@ptr).call(@ptr, attr_ptr)
|
|
119
|
+
|
|
120
|
+
@name, @helpstring, @help_context, @helpfile = read_documentation(@ptr, -1)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def guid
|
|
124
|
+
d1, d2, d3 = @guid_bytes.unpack('LSS')
|
|
125
|
+
d4 = @guid_bytes[8, 8].unpack('C8')
|
|
126
|
+
format('{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}', d1, d2, d3, *d4)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def name
|
|
130
|
+
@helpstring
|
|
131
|
+
end
|
|
132
|
+
alias to_s name
|
|
133
|
+
|
|
134
|
+
def version
|
|
135
|
+
"#{@major}.#{@minor}"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def major_version
|
|
139
|
+
@major
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def minor_version
|
|
143
|
+
@minor
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def visible?
|
|
147
|
+
!(@lib_flags.zero? || (@lib_flags & (LIBFLAG_FRESTRICTED | LIBFLAG_FHIDDEN)) != 0)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def library_name
|
|
151
|
+
@name
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def path
|
|
155
|
+
reg_path_lookup(guid, version, @lcid)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def ole_types
|
|
159
|
+
count = TI.type_info_count_fn(@ptr).call(@ptr)
|
|
160
|
+
Array.new(count) do |i|
|
|
161
|
+
ti_out = ("\x00" * W::PTR_SIZE).b
|
|
162
|
+
hr = TI.type_info_fn(@ptr).call(@ptr, i, ti_out)
|
|
163
|
+
next nil if W.failed?(hr)
|
|
164
|
+
|
|
165
|
+
begin
|
|
166
|
+
WIN32OLE::Type.from_typeinfo_ptr(ti_out.unpack1(W::PACK_PTR))
|
|
167
|
+
rescue WIN32OLE::QueryInterfaceError
|
|
168
|
+
# Matches MRI (ext/win32ole/win32ole_typelib.c's ole_types_from_typelib),
|
|
169
|
+
# which skips entries it can't build a Type for rather than
|
|
170
|
+
# failing the whole array -- one unreadable type in a typelib
|
|
171
|
+
# shouldn't break every other type (and, transitively, every
|
|
172
|
+
# object's #ole_methods) from that same library.
|
|
173
|
+
nil
|
|
174
|
+
end
|
|
175
|
+
end.compact
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def inspect
|
|
179
|
+
"#<WIN32OLE::TypeLib:#{name}>"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
alias ole_classes ole_types
|
|
183
|
+
|
|
184
|
+
# ext/win32ole/win32ole_typelib.c's foletypelib_s_typelibs: every
|
|
185
|
+
# HKCR\TypeLib\{guid}\{version} with a registered name whose file
|
|
186
|
+
# LoadTypeLibEx can open.
|
|
187
|
+
def self.typelibs
|
|
188
|
+
tlibs = []
|
|
189
|
+
with_reg_subkey(TI::HKEY_CLASSES_ROOT, 'TypeLib') do |htypelib|
|
|
190
|
+
each_reg_subkey_name(htypelib) do |guid|
|
|
191
|
+
with_reg_subkey(htypelib, guid) do |hguid|
|
|
192
|
+
each_reg_subkey_name(hguid) do |version|
|
|
193
|
+
next unless with_reg_subkey(hguid, version) { |hversion| reg_default_string_value(hversion) }
|
|
194
|
+
|
|
195
|
+
file = file_under_version_key(hguid, version)
|
|
196
|
+
ptr = file && load_typelib(file)
|
|
197
|
+
next unless ptr
|
|
198
|
+
|
|
199
|
+
begin
|
|
200
|
+
tlibs << from_itypelib_ptr(ptr)
|
|
201
|
+
rescue WIN32OLE::QueryInterfaceError
|
|
202
|
+
# unreadable TLIBATTR; its finalizer still releases ptr
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
tlibs
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def self.each_reg_subkey_name(hkey)
|
|
212
|
+
i = 0
|
|
213
|
+
while (name = TI.reg_enum_key(hkey, i))
|
|
214
|
+
i += 1
|
|
215
|
+
yield name
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Mirrors ext/win32ole/win32ole_typelib.c's typelib_file: resolves a
|
|
220
|
+
# typelib name (e.g. "Microsoft Shell Controls And Automation") or a
|
|
221
|
+
# CLSID string (e.g. a COM class's own CLSID, whose InprocServer32 DLL
|
|
222
|
+
# commonly embeds the same typelib) to the file LoadTypeLibEx needs.
|
|
223
|
+
# Used by WIN32OLE::Type.new's name-based lookup. Returns nil if
|
|
224
|
+
# neither resolves -- the caller then tries the original string
|
|
225
|
+
# itself as a literal file path, same as the C fallback.
|
|
226
|
+
def self.file_for(typelib)
|
|
227
|
+
file_from_clsid(typelib) || file_from_name(typelib)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def self.file_from_clsid(clsid_str)
|
|
231
|
+
with_reg_subkey(TI::HKEY_CLASSES_ROOT, "CLSID\\#{clsid_str}\\InprocServer32") do |hkey|
|
|
232
|
+
reg_default_string_value(hkey)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def self.file_from_name(name)
|
|
237
|
+
with_reg_subkey(TI::HKEY_CLASSES_ROOT, 'TypeLib') do |htypelib|
|
|
238
|
+
result = nil
|
|
239
|
+
i = 0
|
|
240
|
+
while (guid = TI.reg_enum_key(htypelib, i))
|
|
241
|
+
i += 1
|
|
242
|
+
result = file_from_name_under_guid(htypelib, guid, name)
|
|
243
|
+
break if result
|
|
244
|
+
end
|
|
245
|
+
result
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def self.file_from_name_under_guid(htypelib, guid, name)
|
|
250
|
+
with_reg_subkey(htypelib, guid) do |hclsid|
|
|
251
|
+
best_version = nil
|
|
252
|
+
best_fver = nil
|
|
253
|
+
i = 0
|
|
254
|
+
while (version = TI.reg_enum_key(hclsid, i))
|
|
255
|
+
i += 1
|
|
256
|
+
fver = version.to_f
|
|
257
|
+
next if best_fver && fver <= best_fver
|
|
258
|
+
|
|
259
|
+
matched = with_reg_subkey(hclsid, version) { |hversion| reg_default_string_value(hversion) == name }
|
|
260
|
+
if matched
|
|
261
|
+
best_fver = fver
|
|
262
|
+
best_version = version
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
best_version && file_under_version_key(hclsid, best_version)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def self.file_under_version_key(hclsid, version)
|
|
271
|
+
with_reg_subkey(hclsid, version) do |hversion|
|
|
272
|
+
result = nil
|
|
273
|
+
i = 0
|
|
274
|
+
while (lang = TI.reg_enum_key(hversion, i))
|
|
275
|
+
i += 1
|
|
276
|
+
result = with_reg_subkey(hversion, lang) { |hlang| reg_typelib_file_path(hlang) }
|
|
277
|
+
break if result
|
|
278
|
+
end
|
|
279
|
+
result
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Under a TypeLib\{guid}\{version}\{lcid} key, the actual file path is
|
|
284
|
+
# the default value of whichever of these architecture subkeys exists.
|
|
285
|
+
def self.reg_typelib_file_path(hlang)
|
|
286
|
+
%w[win64 win32 win16].each do |arch|
|
|
287
|
+
path = with_reg_subkey(hlang, arch) { |hkey| reg_default_string_value(hkey) }
|
|
288
|
+
return path if path
|
|
289
|
+
end
|
|
290
|
+
nil
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def self.reg_default_string_value(hkey)
|
|
294
|
+
empty_name = W.wstr('')
|
|
295
|
+
type_out = ("\x00" * 4).b
|
|
296
|
+
size_out = [520].pack('L')
|
|
297
|
+
data_out = ("\x00" * 520).b
|
|
298
|
+
err = TI.reg_query_value_ex.call(hkey, empty_name, nil, type_out, data_out, size_out)
|
|
299
|
+
type = type_out.unpack1('L')
|
|
300
|
+
return nil unless err.zero? && (type == TI::REG_SZ || type == TI::REG_EXPAND_SZ)
|
|
301
|
+
|
|
302
|
+
value = data_out[0, size_out.unpack1('L')].force_encoding('UTF-16LE').encode('UTF-8').delete("\x00")
|
|
303
|
+
type == TI::REG_EXPAND_SZ ? W.expand_environment_strings(value) : value
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def self.with_reg_subkey(hkey, name)
|
|
307
|
+
out = ("\x00" * W::PTR_SIZE).b
|
|
308
|
+
err = TI.reg_open_key_ex.call(hkey, W.wstr(name), 0, TI::KEY_READ, out)
|
|
309
|
+
return nil unless err.zero?
|
|
310
|
+
|
|
311
|
+
sub = out.unpack1(W::PACK_PTR)
|
|
312
|
+
begin
|
|
313
|
+
yield sub
|
|
314
|
+
ensure
|
|
315
|
+
TI.reg_close_key.call(sub)
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
private
|
|
320
|
+
|
|
321
|
+
def read_documentation(itypelib_ptr, index)
|
|
322
|
+
name_out = ("\x00" * W::PTR_SIZE).b
|
|
323
|
+
docstring_out = ("\x00" * W::PTR_SIZE).b
|
|
324
|
+
helpcontext_out = ("\x00" * 4).b
|
|
325
|
+
helpfile_out = ("\x00" * W::PTR_SIZE).b
|
|
326
|
+
TI.documentation_fn_for_typelib(itypelib_ptr).call(
|
|
327
|
+
itypelib_ptr, index, name_out, docstring_out, helpcontext_out, helpfile_out
|
|
328
|
+
)
|
|
329
|
+
name_bstr = name_out.unpack1(W::PACK_PTR)
|
|
330
|
+
docstring_bstr = docstring_out.unpack1(W::PACK_PTR)
|
|
331
|
+
helpfile_bstr = helpfile_out.unpack1(W::PACK_PTR)
|
|
332
|
+
name = W.bstr_to_s(name_bstr)
|
|
333
|
+
helpstring = W.bstr_to_s(docstring_bstr) || '' # C's WC2VSTR(NULL) is '', not nil
|
|
334
|
+
helpfile = W.bstr_to_s(helpfile_bstr) || ''
|
|
335
|
+
[name_bstr, docstring_bstr, helpfile_bstr].each { |b| W.sys_free_string.call(b) unless b.zero? }
|
|
336
|
+
[name, helpstring, helpcontext_out.unpack1('L'), helpfile]
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def reg_path_lookup(guid_str, version_str, lcid)
|
|
340
|
+
# HKEY_CLASSES_ROOT is itself the merged Classes root, so the subkey
|
|
341
|
+
# path must NOT be prefixed with "SOFTWARE\Classes\" (that prefix is
|
|
342
|
+
# only needed when opening under HKEY_LOCAL_MACHINE/HKEY_CURRENT_USER
|
|
343
|
+
# directly, as MRI's own clsid_from_remote does for the DCOM path).
|
|
344
|
+
%w[win64 win32 win16].each do |arch_key|
|
|
345
|
+
path = reg_path_lookup_one(guid_str, version_str, lcid, arch_key)
|
|
346
|
+
return path if path
|
|
347
|
+
end
|
|
348
|
+
nil
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def reg_path_lookup_one(guid_str, version_str, lcid, arch_key)
|
|
352
|
+
key_path = W.wstr("TypeLib\\#{guid_str}\\#{version_str}\\#{lcid}\\#{arch_key}")
|
|
353
|
+
hkey_out = ("\x00" * W::PTR_SIZE).b
|
|
354
|
+
err = TI.reg_open_key_ex.call(TI::HKEY_CLASSES_ROOT, key_path, 0, TI::KEY_READ, hkey_out)
|
|
355
|
+
return nil unless err.zero?
|
|
356
|
+
|
|
357
|
+
hkey = hkey_out.unpack1(W::PACK_PTR)
|
|
358
|
+
begin
|
|
359
|
+
empty_name = W.wstr('')
|
|
360
|
+
type_out = ("\x00" * 4).b
|
|
361
|
+
size_out = [520].pack('L') # 260 WCHARs, generous for a MAX_PATH-style value
|
|
362
|
+
data_out = ("\x00" * 520).b
|
|
363
|
+
err = TI.reg_query_value_ex.call(hkey, empty_name, nil, type_out, data_out, size_out)
|
|
364
|
+
return nil unless err.zero? && type_out.unpack1('L') == TI::REG_SZ
|
|
365
|
+
|
|
366
|
+
data_out[0, size_out.unpack1('L')].force_encoding('UTF-16LE').encode('UTF-8').delete("\x00")
|
|
367
|
+
ensure
|
|
368
|
+
TI.reg_close_key.call(hkey)
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def install_finalizer
|
|
373
|
+
ptr = @ptr
|
|
374
|
+
release_fn = W.vtable_function(ptr, 2, [W::VOIDP], W::DWORD)
|
|
375
|
+
ObjectSpace.define_finalizer(self, self.class.finalizer(ptr, release_fn))
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# OLE_FREE, deferred on a thread without OLE (as on JRuby); see
|
|
379
|
+
# WIN32OLE.finalizer.
|
|
380
|
+
def self.finalizer(ptr, release_fn)
|
|
381
|
+
proc { W.release_on_ole_thread { release_fn.call(ptr) } unless ptr.zero? }
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'win32ole/ffi/win32'
|
|
4
|
+
require 'win32ole/ffi/typeinfo'
|
|
5
|
+
|
|
6
|
+
class WIN32OLE
|
|
7
|
+
class Variable
|
|
8
|
+
W = Win32
|
|
9
|
+
TI = TypeInfo
|
|
10
|
+
private_constant :W, :TI
|
|
11
|
+
|
|
12
|
+
VARFLAG_FRESTRICTED = 0x80
|
|
13
|
+
VARFLAG_FHIDDEN = 0x40
|
|
14
|
+
VARFLAG_FNONBROWSABLE = 0x400
|
|
15
|
+
VAR_CONST = 2
|
|
16
|
+
|
|
17
|
+
# ext/win32ole/win32ole_variable.c undefines the allocator, so a
|
|
18
|
+
# Variable can't be constructed directly -- only via Type#variables.
|
|
19
|
+
def self.new(*)
|
|
20
|
+
raise TypeError, "allocator undefined for #{self}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.from_typeinfo_ptr(itypeinfo_ptr, index)
|
|
24
|
+
allocate.tap { |var| var.send(:initialize, itypeinfo_ptr, index) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize(itypeinfo_ptr, index)
|
|
28
|
+
vardesc_out = ("\x00" * W::PTR_SIZE).b
|
|
29
|
+
hr = TI.var_desc_fn(itypeinfo_ptr).call(itypeinfo_ptr, index, vardesc_out)
|
|
30
|
+
if W.failed?(hr)
|
|
31
|
+
raise WIN32OLE::QueryInterfaceError, W.query_interface_error_message('GetVarDesc', W.hr_hex(hr))
|
|
32
|
+
end
|
|
33
|
+
vardesc_ptr = vardesc_out.unpack1(W::PACK_PTR)
|
|
34
|
+
vardesc = TI::VARDESC.new(vardesc_ptr)
|
|
35
|
+
|
|
36
|
+
@memid = vardesc.memid
|
|
37
|
+
@type_detail = TI.typedesc_details(itypeinfo_ptr, vardesc_ptr + TI.offsetof(TI::VARDESC, 'tdesc_union_ptr'))
|
|
38
|
+
@var_flags = vardesc.wVarFlags
|
|
39
|
+
@varkind = vardesc.varkind
|
|
40
|
+
@value = read_const_value(vardesc, vardesc_ptr) if @varkind == VAR_CONST
|
|
41
|
+
@name = read_name(itypeinfo_ptr, @memid)
|
|
42
|
+
|
|
43
|
+
TI.release_var_desc_fn(itypeinfo_ptr).call(itypeinfo_ptr, vardesc_ptr)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def name
|
|
47
|
+
@name
|
|
48
|
+
end
|
|
49
|
+
alias to_s name
|
|
50
|
+
|
|
51
|
+
def ole_type
|
|
52
|
+
@type_detail.last
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def ole_type_detail
|
|
56
|
+
@type_detail.dup
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def value
|
|
60
|
+
@value
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def visible?
|
|
64
|
+
(@var_flags & (VARFLAG_FRESTRICTED | VARFLAG_FHIDDEN | VARFLAG_FNONBROWSABLE)) == 0
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def variable_kind
|
|
68
|
+
TI::VARKIND_NAMES.fetch(@varkind, 'UNKNOWN')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def varkind
|
|
72
|
+
@varkind
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def inspect
|
|
76
|
+
"#<WIN32OLE::Variable:#{name}=#{value.inspect}>"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def read_const_value(vardesc, vardesc_ptr)
|
|
82
|
+
# VARDESC.union_oInst_or_lpvarValue holds a VARIANT* when varkind == VAR_CONST.
|
|
83
|
+
variant_ptr = vardesc.union_oInst_or_lpvarValue
|
|
84
|
+
return nil if variant_ptr.to_i.zero?
|
|
85
|
+
|
|
86
|
+
variant_bytes = Fiddle::Pointer.new(variant_ptr)[0, W::VARIANT_SIZE]
|
|
87
|
+
vt, payload = W.unpack_variant(variant_bytes)
|
|
88
|
+
type = W.variant_ruby_type(vt)
|
|
89
|
+
case type
|
|
90
|
+
when :i4 then W.unpack_i4(payload)
|
|
91
|
+
when :i8 then W.unpack_i8(payload)
|
|
92
|
+
when :r8 then W.unpack_r8(payload)
|
|
93
|
+
when :bool then W.unpack_bool(payload)
|
|
94
|
+
when :bstr then W.bstr_to_s(W.unpack_pointer(payload))
|
|
95
|
+
else nil
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def read_name(itypeinfo_ptr, memid)
|
|
100
|
+
names_out = ("\x00" * W::PTR_SIZE).b
|
|
101
|
+
count_out = ("\x00" * 4).b
|
|
102
|
+
get_names_fn(itypeinfo_ptr).call(itypeinfo_ptr, memid, names_out, 1, count_out)
|
|
103
|
+
bstr = names_out.unpack1(W::PACK_PTR)
|
|
104
|
+
name = W.bstr_to_s(bstr)
|
|
105
|
+
W.sys_free_string.call(bstr) unless bstr.zero?
|
|
106
|
+
name
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def get_names_fn(itypeinfo_ptr)
|
|
110
|
+
@@get_names_fns ||= {}
|
|
111
|
+
@@get_names_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
|
|
112
|
+
itypeinfo_ptr, TI::ITYPEINFO_VTBL[:GetNames],
|
|
113
|
+
[W::VOIDP, W::LONG, W::VOIDP, W::DWORD, W::VOIDP], W::LONG
|
|
114
|
+
)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|