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,337 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'win32ole/ffi/win32'
|
|
4
|
+
require 'win32ole/ffi/typeinfo'
|
|
5
|
+
require 'win32ole/ffi/method'
|
|
6
|
+
require 'win32ole/ffi/variable'
|
|
7
|
+
|
|
8
|
+
class WIN32OLE
|
|
9
|
+
class Type
|
|
10
|
+
W = Win32
|
|
11
|
+
TI = TypeInfo
|
|
12
|
+
private_constant :W, :TI
|
|
13
|
+
|
|
14
|
+
TYPEFLAG_FHIDDEN = 0x10
|
|
15
|
+
TYPEFLAG_FRESTRICTED = 0x200
|
|
16
|
+
TKIND_ALIAS = 6
|
|
17
|
+
|
|
18
|
+
# Mirrors ext/win32ole/win32ole_type.c's foletype_initialize:
|
|
19
|
+
# WIN32OLE::Type.new(typelib, ole_class) resolves +typelib+ (a
|
|
20
|
+
# registered typelib name or a CLSID string) to a file via
|
|
21
|
+
# TypeLib.file_for, LoadTypeLibEx's it, then finds the ole_class
|
|
22
|
+
# member by name.
|
|
23
|
+
def self.new(typelib, ole_class)
|
|
24
|
+
typelib = check_string!(typelib)
|
|
25
|
+
ole_class = check_string!(ole_class)
|
|
26
|
+
|
|
27
|
+
itypelib_ptr = load_typelib_for(typelib)
|
|
28
|
+
begin
|
|
29
|
+
found = find_type_by_name(itypelib_ptr, ole_class)
|
|
30
|
+
raise WIN32OLE::RuntimeError, "not found `#{ole_class}` in `#{typelib}`" if found.nil?
|
|
31
|
+
|
|
32
|
+
found
|
|
33
|
+
ensure
|
|
34
|
+
W.vtable_function(itypelib_ptr, 2, [W::VOIDP], W::DWORD).call(itypelib_ptr)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.check_string!(val)
|
|
39
|
+
return val if val.is_a?(::String)
|
|
40
|
+
return val.to_str if val.respond_to?(:to_str)
|
|
41
|
+
|
|
42
|
+
raise TypeError, "no implicit conversion of #{val.class} into String"
|
|
43
|
+
end
|
|
44
|
+
private_class_method :check_string!
|
|
45
|
+
|
|
46
|
+
def self.load_typelib_for(typelib)
|
|
47
|
+
file = WIN32OLE::TypeLib.file_for(typelib) || typelib
|
|
48
|
+
out = ("\x00" * W::PTR_SIZE).b
|
|
49
|
+
hr = TI.load_type_lib_ex.call(W.wstr(file), TI::REGKIND_NONE, out)
|
|
50
|
+
raise WIN32OLE::RuntimeError, 'failed to LoadTypeLibEx' if W.failed?(hr)
|
|
51
|
+
|
|
52
|
+
out.unpack1(W::PACK_PTR)
|
|
53
|
+
end
|
|
54
|
+
private_class_method :load_typelib_for
|
|
55
|
+
|
|
56
|
+
# Mirrors oleclass_from_typelib: scan the typelib's members by
|
|
57
|
+
# GetDocumentation name first (cheap), and only GetTypeInfo (which
|
|
58
|
+
# AddRefs the result) once a name actually matches.
|
|
59
|
+
def self.find_type_by_name(itypelib_ptr, ole_class)
|
|
60
|
+
count = TI.type_info_count_fn(itypelib_ptr).call(itypelib_ptr)
|
|
61
|
+
count.times do |i|
|
|
62
|
+
name_out = ("\x00" * W::PTR_SIZE).b
|
|
63
|
+
hr = TI.documentation_fn_for_typelib(itypelib_ptr).call(itypelib_ptr, i, name_out, nil, nil, nil)
|
|
64
|
+
next if W.failed?(hr)
|
|
65
|
+
|
|
66
|
+
name_bstr = name_out.unpack1(W::PACK_PTR)
|
|
67
|
+
name = W.bstr_to_s(name_bstr)
|
|
68
|
+
W.sys_free_string.call(name_bstr) unless name_bstr.zero?
|
|
69
|
+
next unless name == ole_class
|
|
70
|
+
|
|
71
|
+
ti_out = ("\x00" * W::PTR_SIZE).b
|
|
72
|
+
hr = TI.type_info_fn(itypelib_ptr).call(itypelib_ptr, i, ti_out)
|
|
73
|
+
return nil if W.failed?(hr)
|
|
74
|
+
|
|
75
|
+
return from_typeinfo_ptr(ti_out.unpack1(W::PACK_PTR))
|
|
76
|
+
end
|
|
77
|
+
nil
|
|
78
|
+
end
|
|
79
|
+
private_class_method :find_type_by_name
|
|
80
|
+
|
|
81
|
+
def self.from_typeinfo_ptr(itypeinfo_ptr)
|
|
82
|
+
allocate.tap { |type| type.send(:initialize, itypeinfo_ptr) }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def initialize(itypeinfo_ptr)
|
|
86
|
+
# As the C allocator does, so the finalizer's ole_initialized? check
|
|
87
|
+
# holds on the thread that made this object.
|
|
88
|
+
WIN32OLE.ole_initialize
|
|
89
|
+
@ptr = itypeinfo_ptr
|
|
90
|
+
# Install the finalizer before any call that could raise: the caller
|
|
91
|
+
# has already AddRef'd this pointer, so if GetTypeAttr/read_documentation
|
|
92
|
+
# (or the vtable lookups they perform) raise, this Release must still
|
|
93
|
+
# happen -- otherwise the reference leaks permanently.
|
|
94
|
+
install_finalizer
|
|
95
|
+
|
|
96
|
+
attr_out = ("\x00" * W::PTR_SIZE).b
|
|
97
|
+
hr = TI.type_attr_fn(@ptr).call(@ptr, attr_out)
|
|
98
|
+
if W.failed?(hr)
|
|
99
|
+
raise WIN32OLE::QueryInterfaceError, W.query_interface_error_message('GetTypeAttr', W.hr_hex(hr))
|
|
100
|
+
end
|
|
101
|
+
attr_ptr = attr_out.unpack1(W::PACK_PTR)
|
|
102
|
+
typeattr = TI::TYPEATTR.new(attr_ptr)
|
|
103
|
+
|
|
104
|
+
@guid_bytes = [typeattr.guid_Data1, typeattr.guid_Data2, typeattr.guid_Data3].pack('LSS') +
|
|
105
|
+
typeattr.guid_Data4.pack('C8')
|
|
106
|
+
@typekind = typeattr.typekind
|
|
107
|
+
@major = typeattr.wMajorVerNum
|
|
108
|
+
@minor = typeattr.wMinorVerNum
|
|
109
|
+
@type_flags = typeattr.wTypeFlags
|
|
110
|
+
# ole_type_src_type: the aliased type, resolved now (while TYPEATTR is
|
|
111
|
+
# alive) through the raw TYPEDESC -- reading its union through the
|
|
112
|
+
# struct's `void *` accessor overflows on the garbage upper bytes it
|
|
113
|
+
# holds for anything but a real pointer.
|
|
114
|
+
@src_type =
|
|
115
|
+
if @typekind == TKIND_ALIAS
|
|
116
|
+
TI.typedesc_details(@ptr, attr_ptr + TI.offsetof(TI::TYPEATTR, 'tdescAlias_union_ptr')).last
|
|
117
|
+
end
|
|
118
|
+
TI.release_type_attr_fn(@ptr).call(@ptr, attr_ptr)
|
|
119
|
+
|
|
120
|
+
@name, @helpstring, @help_context, @helpfile = read_documentation(@ptr, -1)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# WIN32OLE::Method.new(oletype, name) needs this raw ITypeInfo* to
|
|
124
|
+
# search oletype's own funcs and its implemented interfaces -- same
|
|
125
|
+
# deliberate, documented crack in encapsulation as WIN32OLE#dispatch_ptr.
|
|
126
|
+
def itypeinfo_ptr
|
|
127
|
+
@ptr
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def name
|
|
131
|
+
@name
|
|
132
|
+
end
|
|
133
|
+
alias to_s name
|
|
134
|
+
|
|
135
|
+
def ole_type
|
|
136
|
+
TI::TYPEKIND_NAMES[@typekind]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def guid
|
|
140
|
+
d1, d2, d3 = @guid_bytes.unpack('LSS')
|
|
141
|
+
d4 = @guid_bytes[8, 8].unpack('C8')
|
|
142
|
+
format('{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}', d1, d2, d3, *d4)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def progid
|
|
146
|
+
W.prog_id_from_clsid(@guid_bytes)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def visible?
|
|
150
|
+
(@type_flags & (TYPEFLAG_FHIDDEN | TYPEFLAG_FRESTRICTED)) == 0
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def major_version
|
|
154
|
+
@major
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def minor_version
|
|
158
|
+
@minor
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def typekind
|
|
162
|
+
@typekind
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def helpstring
|
|
166
|
+
@helpstring
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def helpfile
|
|
170
|
+
@helpfile
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def helpcontext
|
|
174
|
+
@help_context
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def src_type
|
|
178
|
+
@src_type
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def variables
|
|
182
|
+
count = type_attr_var_count
|
|
183
|
+
Array.new(count) { |i| WIN32OLE::Variable.from_typeinfo_ptr(@ptr, i) }
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# ole_methods_from_typeinfo: the type's own functions, then (one level)
|
|
187
|
+
# those of every type it implements -- a coclass such as "Shell" has
|
|
188
|
+
# none of its own.
|
|
189
|
+
def ole_methods
|
|
190
|
+
methods = Array.new(type_attr_func_count) { |i| WIN32OLE::Method.from_typeinfo_ptr(@ptr, i) }
|
|
191
|
+
TI.each_impl_type(@ptr) do |_flags, ref_ptr|
|
|
192
|
+
begin
|
|
193
|
+
(TI.type_attr_value(ref_ptr, :cFuncs) || 0).times do |i|
|
|
194
|
+
methods << WIN32OLE::Method.from_typeinfo_ptr(ref_ptr, i, @ptr)
|
|
195
|
+
end
|
|
196
|
+
ensure
|
|
197
|
+
TI.release(ref_ptr)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
methods
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def ole_typelib
|
|
204
|
+
tlib_out = ("\x00" * W::PTR_SIZE).b
|
|
205
|
+
index_out = ("\x00" * 4).b
|
|
206
|
+
hr = TI.containing_typelib_fn(@ptr).call(@ptr, tlib_out, index_out)
|
|
207
|
+
if W.failed?(hr)
|
|
208
|
+
raise WIN32OLE::QueryInterfaceError, W.query_interface_error_message('GetContainingTypeLib', W.hr_hex(hr))
|
|
209
|
+
end
|
|
210
|
+
WIN32OLE::TypeLib.from_itypelib_ptr(tlib_out.unpack1(W::PACK_PTR))
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def inspect
|
|
214
|
+
"#<WIN32OLE::Type:#{name}>"
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def implemented_ole_types
|
|
218
|
+
impl_ole_types(0)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def source_ole_types
|
|
222
|
+
impl_ole_types(TI::IMPLTYPEFLAG_FSOURCE)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def default_event_sources
|
|
226
|
+
impl_ole_types(TI::IMPLTYPEFLAG_FSOURCE | TI::IMPLTYPEFLAG_FDEFAULT)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def default_ole_types
|
|
230
|
+
impl_ole_types(TI::IMPLTYPEFLAG_FDEFAULT)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# foletype_s_ole_classes (obsolete in C too)
|
|
234
|
+
def self.ole_classes(typelib)
|
|
235
|
+
WIN32OLE::TypeLib.new(typelib).ole_types
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# foletype_s_typelibs (obsolete in C too)
|
|
239
|
+
def self.typelibs
|
|
240
|
+
WIN32OLE::TypeLib.typelibs.map(&:name)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# foletype_s_progids: the ProgID and VersionIndependentProgID of every
|
|
244
|
+
# HKCR\CLSID entry that has them.
|
|
245
|
+
def self.progids
|
|
246
|
+
tl = WIN32OLE::TypeLib
|
|
247
|
+
progids = []
|
|
248
|
+
tl.with_reg_subkey(TI::HKEY_CLASSES_ROOT, 'CLSID') do |hclsids|
|
|
249
|
+
tl.each_reg_subkey_name(hclsids) do |clsid|
|
|
250
|
+
tl.with_reg_subkey(hclsids, clsid) do |hclsid|
|
|
251
|
+
%w[ProgID VersionIndependentProgID].each do |key|
|
|
252
|
+
progid = tl.with_reg_subkey(hclsid, key) { |hkey| tl.reg_default_string_value(hkey) }
|
|
253
|
+
progids << progid if progid
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
progids
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
private
|
|
262
|
+
|
|
263
|
+
# ole_type_impl_ole_types: implemented types whose IMPLTYPEFLAGs include
|
|
264
|
+
# all of +implflags+. A matching ref ITypeInfo* is handed to the new
|
|
265
|
+
# Type (whose finalizer releases it); the rest are released here.
|
|
266
|
+
def impl_ole_types(implflags)
|
|
267
|
+
types = []
|
|
268
|
+
TI.each_impl_type(@ptr) do |flags, ref_ptr|
|
|
269
|
+
if (flags & implflags) == implflags
|
|
270
|
+
begin
|
|
271
|
+
types << Type.from_typeinfo_ptr(ref_ptr)
|
|
272
|
+
rescue WIN32OLE::QueryInterfaceError
|
|
273
|
+
# like ole_type_from_itypeinfo returning nil: skip it (the
|
|
274
|
+
# Type's finalizer, installed first, still releases ref_ptr)
|
|
275
|
+
end
|
|
276
|
+
else
|
|
277
|
+
TI.release(ref_ptr)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
types
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def type_attr_func_count
|
|
284
|
+
attr_out = ("\x00" * W::PTR_SIZE).b
|
|
285
|
+
hr = TI.type_attr_fn(@ptr).call(@ptr, attr_out)
|
|
286
|
+
if W.failed?(hr)
|
|
287
|
+
raise WIN32OLE::QueryInterfaceError, W.query_interface_error_message('GetTypeAttr', W.hr_hex(hr))
|
|
288
|
+
end
|
|
289
|
+
attr_ptr = attr_out.unpack1(W::PACK_PTR)
|
|
290
|
+
count = TI::TYPEATTR.new(attr_ptr).cFuncs
|
|
291
|
+
TI.release_type_attr_fn(@ptr).call(@ptr, attr_ptr)
|
|
292
|
+
count
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def type_attr_var_count
|
|
296
|
+
attr_out = ("\x00" * W::PTR_SIZE).b
|
|
297
|
+
hr = TI.type_attr_fn(@ptr).call(@ptr, attr_out)
|
|
298
|
+
if W.failed?(hr)
|
|
299
|
+
raise WIN32OLE::QueryInterfaceError, W.query_interface_error_message('GetTypeAttr', W.hr_hex(hr))
|
|
300
|
+
end
|
|
301
|
+
attr_ptr = attr_out.unpack1(W::PACK_PTR)
|
|
302
|
+
count = TI::TYPEATTR.new(attr_ptr).cVars
|
|
303
|
+
TI.release_type_attr_fn(@ptr).call(@ptr, attr_ptr)
|
|
304
|
+
count
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def read_documentation(itypeinfo_ptr, memid)
|
|
308
|
+
name_out = ("\x00" * W::PTR_SIZE).b
|
|
309
|
+
docstring_out = ("\x00" * W::PTR_SIZE).b
|
|
310
|
+
helpcontext_out = ("\x00" * 4).b
|
|
311
|
+
helpfile_out = ("\x00" * W::PTR_SIZE).b
|
|
312
|
+
TI.documentation_fn_for_typeinfo(itypeinfo_ptr).call(
|
|
313
|
+
itypeinfo_ptr, memid, name_out, docstring_out, helpcontext_out, helpfile_out
|
|
314
|
+
)
|
|
315
|
+
name_bstr = name_out.unpack1(W::PACK_PTR)
|
|
316
|
+
docstring_bstr = docstring_out.unpack1(W::PACK_PTR)
|
|
317
|
+
helpfile_bstr = helpfile_out.unpack1(W::PACK_PTR)
|
|
318
|
+
name = W.bstr_to_s(name_bstr)
|
|
319
|
+
helpstring = W.bstr_to_s(docstring_bstr) || '' # C's WC2VSTR(NULL) is '', not nil
|
|
320
|
+
helpfile = W.bstr_to_s(helpfile_bstr) || ''
|
|
321
|
+
[name_bstr, docstring_bstr, helpfile_bstr].each { |b| W.sys_free_string.call(b) unless b.zero? }
|
|
322
|
+
[name, helpstring, helpcontext_out.unpack1('L'), helpfile]
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def install_finalizer
|
|
326
|
+
ptr = @ptr
|
|
327
|
+
release_fn = W.vtable_function(ptr, 2, [W::VOIDP], W::DWORD)
|
|
328
|
+
ObjectSpace.define_finalizer(self, self.class.finalizer(ptr, release_fn))
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# OLE_FREE, deferred on a thread without OLE (as on JRuby); see
|
|
332
|
+
# WIN32OLE.finalizer.
|
|
333
|
+
def self.finalizer(ptr, release_fn)
|
|
334
|
+
proc { W.release_on_ole_thread { release_fn.call(ptr) } unless ptr.zero? }
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
end
|