win32ole-ffi 0.1.0-java

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.
@@ -0,0 +1,488 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fiddle'
4
+ require 'fiddle/import'
5
+ require 'win32ole/ffi/win32'
6
+
7
+ class WIN32OLE
8
+ module TypeInfo
9
+ extend Fiddle::Importer
10
+
11
+ # Each struct's [types, members], for layout_offsetof: Ruby 2.6/2.7's
12
+ # fiddle struct classes don't keep them.
13
+ LAYOUTS = {}
14
+ private_constant :LAYOUTS
15
+
16
+ def self.struct(signature)
17
+ super.tap { |klass| LAYOUTS[klass] = parse_struct_signature(signature, type_alias) }
18
+ end
19
+
20
+ GUID = struct([
21
+ 'unsigned int Data1', 'unsigned short Data2', 'unsigned short Data3',
22
+ 'unsigned char Data4[8]'
23
+ ])
24
+
25
+ TYPEDESC = struct([
26
+ 'void *union_ptr', # lptdesc / lpadesc / hreftype share this slot;
27
+ # caller's context determines which interpretation applies
28
+ 'unsigned short vt'
29
+ ])
30
+
31
+ ELEMDESC = struct([
32
+ 'void *tdesc_union_ptr', 'unsigned short tdesc_vt', # TYPEDESC tdesc, inlined
33
+ 'void *paramdescex_ptr', 'unsigned short wParamFlags' # PARAMDESC/IDLDESC union
34
+ ])
35
+
36
+ FUNCDESC = struct([
37
+ 'int memid', 'char _pad1[4]', # memid + padding to align lprgscode
38
+ 'void *lprgscode', 'void *lprgelemdescParam',
39
+ 'int funckind', 'int invkind', 'int callconv',
40
+ 'short cParams', 'short cParamsOpt', 'short oVft', 'short cScodes',
41
+ 'char _pad2[4]', # padding to align elemdescFunc
42
+ # elemdescFunc (ELEMDESC, inlined) — same 4-field shape as ELEMDESC above
43
+ 'void *ret_tdesc_union_ptr', 'unsigned short ret_tdesc_vt', 'char _pad3[6]',
44
+ 'void *ret_paramdescex_ptr', 'unsigned short ret_wParamFlags', 'char _pad4[6]',
45
+ 'unsigned short wFuncFlags', 'char _pad5[6]'
46
+ ])
47
+
48
+ VARDESC = struct([
49
+ 'int memid', 'char _pad1[4]', # memid + padding to align lpstrSchema
50
+ 'void *lpstrSchema', 'void *union_oInst_or_lpvarValue',
51
+ # elemdescVar (ELEMDESC, inlined) — full 32-byte block: TYPEDESC (16) + PARAMDESC/IDLDESC (16)
52
+ 'void *tdesc_union_ptr', 'unsigned short tdesc_vt', 'char _pad2[6]', # TYPEDESC: 8+2+6=16
53
+ 'void *paramdescex_ptr', 'unsigned short wParamFlags', 'char _pad3[6]', # PARAMDESC/IDLDESC: 8+2+6=16
54
+ 'unsigned short wVarFlags', 'int varkind'
55
+ ])
56
+
57
+ TYPEATTR = struct([
58
+ 'unsigned int guid_Data1', 'unsigned short guid_Data2',
59
+ 'unsigned short guid_Data3', 'unsigned char guid_Data4[8]',
60
+ 'unsigned int lcid', 'unsigned int dwReserved',
61
+ 'int memidConstructor', 'int memidDestructor',
62
+ 'void *lpstrSchema', 'unsigned int cbSizeInstance',
63
+ 'int typekind', 'unsigned short cFuncs', 'unsigned short cVars',
64
+ 'unsigned short cImplTypes', 'unsigned short cbSizeVft',
65
+ 'unsigned short cbAlignment', 'unsigned short wTypeFlags',
66
+ 'unsigned short wMajorVerNum', 'unsigned short wMinorVerNum',
67
+ # tdescAlias (TYPEDESC, inlined) — complete 16-byte block with trailing padding
68
+ 'void *tdescAlias_union_ptr', 'unsigned short tdescAlias_vt', 'char _pad1[6]',
69
+ # idldescType (IDLDESC: ULONG_PTR dwReserved + USHORT wIDLFlags)
70
+ 'void *idldescType_dwReserved', 'unsigned short idldescType_wIDLFlags'
71
+ ])
72
+
73
+ ITYPEINFO_VTBL = {
74
+ GetTypeAttr: 3, GetFuncDesc: 5, GetVarDesc: 6, GetNames: 7,
75
+ GetRefTypeOfImplType: 8, GetImplTypeFlags: 9, GetIDsOfNames: 10,
76
+ Invoke: 11, GetDocumentation: 12, GetDllEntry: 13, GetRefTypeInfo: 14,
77
+ AddressOfMember: 15, CreateInstance: 16, GetMops: 17,
78
+ GetContainingTypeLib: 18, ReleaseTypeAttr: 19, ReleaseFuncDesc: 20,
79
+ ReleaseVarDesc: 21
80
+ }.freeze
81
+
82
+ ITYPELIB_VTBL = {
83
+ GetTypeInfoCount: 3, GetTypeInfo: 4, GetTypeInfoType: 5,
84
+ GetTypeInfoOfGuid: 6, GetLibAttr: 7, GetTypeComp: 8,
85
+ GetDocumentation: 9, IsName: 10, FindName: 11, ReleaseTLibAttr: 12
86
+ }.freeze
87
+
88
+ # Fiddle::CStruct.offsetof is fiddle 1.1+; Ruby 2.6-3.0's fiddle (1.0.x)
89
+ # lacks it. JRuby's (1.1.4+) has it.
90
+ def self.offsetof(struct, name)
91
+ struct.respond_to?(:offsetof) ? struct.offsetof(name) : layout_offsetof(struct, name)
92
+ end
93
+
94
+ # Lays the members out as Fiddle::CStructEntity#set_ctypes does.
95
+ def self.layout_offsetof(struct, name)
96
+ offset = 0
97
+ types, members = LAYOUTS.fetch(struct)
98
+ types.zip(members) do |(type, count), member|
99
+ offset = Fiddle::PackInfo.align(offset, Fiddle::PackInfo::ALIGN_MAP[type])
100
+ return offset if member == name
101
+
102
+ offset += Fiddle::PackInfo::SIZE_MAP[type] * (count || 1)
103
+ end
104
+ nil
105
+ end
106
+
107
+ TYPEKIND_NAMES = {
108
+ 0 => 'Enum', 1 => 'Record', 2 => 'Module', 3 => 'Interface',
109
+ 4 => 'Dispatch', 5 => 'Class', 6 => 'Alias', 7 => 'Union', 8 => 'Max'
110
+ }.freeze
111
+
112
+ VARKIND_NAMES = {
113
+ 0 => 'PERINSTANCE', 1 => 'STATIC', 2 => 'CONSTANT', 3 => 'DISPATCH'
114
+ }.freeze
115
+
116
+ # Transcribed from MRI's ole_typedesc2val (ext/win32ole/win32ole.c) —
117
+ # introspection type names, distinct from Win32.variant_ruby_type's much
118
+ # smaller table (which maps the few VARTYPEs the marshaling code
119
+ # actually needs to send/receive, not the full set a real type library
120
+ # can describe). VT_DISPATCH(9) and VT_UNKNOWN(13) are genuinely
121
+ # different values with genuinely different names — do not conflate them.
122
+ VARTYPE_NAMES = {
123
+ 0 => 'EMPTY', 1 => 'NULL', 2 => 'I2', 3 => 'I4', 4 => 'R4', 5 => 'R8',
124
+ 6 => 'CY', 7 => 'DATE', 8 => 'BSTR', 9 => 'DISPATCH', 10 => 'ERROR',
125
+ 11 => 'BOOL', 12 => 'VARIANT', 13 => 'UNKNOWN', 14 => 'DECIMAL',
126
+ 16 => 'I1', 17 => 'UI1', 18 => 'UI2', 19 => 'UI4', 20 => 'I8', 21 => 'UI8',
127
+ 22 => 'INT', 23 => 'UINT', 24 => 'VOID', 25 => 'HRESULT', 26 => 'PTR',
128
+ 27 => 'SAFEARRAY', 28 => 'CARRAY', 29 => 'USERDEFINED', 30 => 'LPSTR',
129
+ 31 => 'LPWSTR', 36 => 'RECORD'
130
+ }.freeze
131
+
132
+ INVOKE_FUNC = 0x1
133
+ INVOKE_PROPERTYGET = 0x2
134
+ INVOKE_PROPERTYPUT = 0x4
135
+ INVOKE_PROPERTYPUTREF = 0x8
136
+
137
+ module_function
138
+
139
+ def vartype_name(vt)
140
+ VARTYPE_NAMES.fetch(vt) { "Unknown Type #{vt}" }
141
+ end
142
+
143
+ VT_PTR = 26
144
+ VT_SAFEARRAY = 27
145
+ VT_USERDEFINED = 29
146
+ MEMBERID_NIL = -1
147
+
148
+ # Mirrors ext/win32ole/win32ole.c's ole_typedesc2val: the type-detail
149
+ # chain for the TYPEDESC at tdesc_addr, e.g. ["PTR", "USERDEFINED",
150
+ # "Folder"]. Its last element is what return_type/ole_type report.
151
+ # The union is read as raw bytes, not through a struct's `void *`
152
+ # accessor: for VT_USERDEFINED only its low 4 bytes (the HREFTYPE) are
153
+ # meaningful and the rest can be garbage that overflows a pointer.
154
+ def typedesc_details(itypeinfo_ptr, tdesc_addr, details = [])
155
+ tdesc = Fiddle::Pointer.new(tdesc_addr)
156
+ union = tdesc[0, W::PTR_SIZE].unpack1(W::PACK_PTR)
157
+ vt = tdesc[W::PTR_SIZE, 2].unpack1('S')
158
+ details << vartype_name(vt)
159
+ case vt
160
+ when VT_PTR, VT_SAFEARRAY
161
+ typedesc_details(itypeinfo_ptr, union, details)
162
+ when VT_USERDEFINED
163
+ name = ref_type_name(itypeinfo_ptr, union & 0xFFFFFFFF)
164
+ details << name if name
165
+ details
166
+ else
167
+ details
168
+ end
169
+ end
170
+
171
+ def ref_type_name(itypeinfo_ptr, hreftype)
172
+ ref_out = ("\x00" * W::PTR_SIZE).b
173
+ return nil if W.failed?(ref_type_info_fn(itypeinfo_ptr).call(itypeinfo_ptr, hreftype, ref_out))
174
+
175
+ ref_ptr = ref_out.unpack1(W::PACK_PTR)
176
+ begin
177
+ type_name(ref_ptr)
178
+ ensure
179
+ release(ref_ptr)
180
+ end
181
+ end
182
+
183
+ # The type's own name (GetDocumentation(MEMBERID_NIL)), or nil.
184
+ def type_name(itypeinfo_ptr)
185
+ member_name(itypeinfo_ptr, MEMBERID_NIL)
186
+ end
187
+
188
+ def member_name(itypeinfo_ptr, memid)
189
+ name_out = ("\x00" * W::PTR_SIZE).b
190
+ hr = documentation_fn_for_typeinfo(itypeinfo_ptr).call(itypeinfo_ptr, memid, name_out, nil, nil, nil)
191
+ return nil if W.failed?(hr)
192
+
193
+ bstr = name_out.unpack1(W::PACK_PTR)
194
+ name = W.bstr_to_s(bstr)
195
+ W.sys_free_string.call(bstr) unless bstr.zero?
196
+ name
197
+ end
198
+
199
+ def release(iunknown_ptr)
200
+ W.vtable_function(iunknown_ptr, 2, [W::VOIDP], W::DWORD).call(iunknown_ptr)
201
+ end
202
+
203
+ TKIND_COCLASS = 5
204
+ IMPLTYPEFLAG_FDEFAULT = 0x1
205
+ IMPLTYPEFLAG_FSOURCE = 0x2
206
+
207
+ # Reads one TYPEATTR field (:typekind, :cFuncs, :cVars, :cImplTypes...),
208
+ # or nil if GetTypeAttr fails.
209
+ def type_attr_value(itypeinfo_ptr, field)
210
+ attr_out = ("\x00" * W::PTR_SIZE).b
211
+ return nil if W.failed?(type_attr_fn(itypeinfo_ptr).call(itypeinfo_ptr, attr_out))
212
+
213
+ attr_ptr = attr_out.unpack1(W::PACK_PTR)
214
+ value = TYPEATTR.new(attr_ptr).send(field)
215
+ release_type_attr_fn(itypeinfo_ptr).call(itypeinfo_ptr, attr_ptr)
216
+ value
217
+ end
218
+
219
+ # Yields [impl_type_flags, ref_itypeinfo_ptr] for each implemented type
220
+ # of itypeinfo_ptr, skipping ones whose flags or ITypeInfo can't be
221
+ # read, the same loop ole_type_impl_ole_types / ole_methods_from_typeinfo
222
+ # use. The block owns ref_itypeinfo_ptr: it must release it or hand
223
+ # it to something that will.
224
+ def each_impl_type(itypeinfo_ptr)
225
+ (type_attr_value(itypeinfo_ptr, :cImplTypes) || 0).times do |i|
226
+ flags_out = ("\x00" * 4).b
227
+ next if W.failed?(impl_type_flags_fn(itypeinfo_ptr).call(itypeinfo_ptr, i, flags_out))
228
+
229
+ href_out = ("\x00" * 4).b
230
+ next if W.failed?(ref_type_of_impl_type_fn(itypeinfo_ptr).call(itypeinfo_ptr, i, href_out))
231
+
232
+ ref_out = ("\x00" * W::PTR_SIZE).b
233
+ next if W.failed?(ref_type_info_fn(itypeinfo_ptr).call(itypeinfo_ptr, href_out.unpack1('L'), ref_out))
234
+
235
+ yield flags_out.unpack1('l'), ref_out.unpack1(W::PACK_PTR)
236
+ end
237
+ end
238
+
239
+ # ext/win32ole/win32ole_method.c's ole_method_event: true if owner_ptr
240
+ # is a coclass and one of its source interfaces has, at method_index, a
241
+ # function named method_name.
242
+ def method_event?(owner_ptr, method_index, method_name)
243
+ return false unless type_attr_value(owner_ptr, :typekind) == TKIND_COCLASS
244
+
245
+ each_impl_type(owner_ptr) do |flags, ref_ptr|
246
+ begin
247
+ next if (flags & IMPLTYPEFLAG_FSOURCE).zero?
248
+
249
+ funcdesc_out = ("\x00" * W::PTR_SIZE).b
250
+ next if W.failed?(func_desc_fn(ref_ptr).call(ref_ptr, method_index, funcdesc_out))
251
+
252
+ funcdesc_ptr = funcdesc_out.unpack1(W::PACK_PTR)
253
+ memid = FUNCDESC.new(funcdesc_ptr).memid
254
+ release_func_desc_fn(ref_ptr).call(ref_ptr, funcdesc_ptr)
255
+ return true if member_name(ref_ptr, memid) == method_name
256
+ ensure
257
+ release(ref_ptr)
258
+ end
259
+ end
260
+ false
261
+ end
262
+
263
+ def invoke_kind_name(invkind)
264
+ if (invkind & INVOKE_PROPERTYGET != 0) && (invkind & INVOKE_PROPERTYPUT != 0)
265
+ 'PROPERTY'
266
+ elsif invkind & INVOKE_PROPERTYGET != 0
267
+ 'PROPERTYGET'
268
+ elsif invkind & INVOKE_PROPERTYPUT != 0
269
+ 'PROPERTYPUT'
270
+ elsif invkind & INVOKE_PROPERTYPUTREF != 0
271
+ 'PROPERTYPUTREF'
272
+ elsif invkind & INVOKE_FUNC != 0
273
+ 'FUNC'
274
+ else
275
+ 'UNKNOWN'
276
+ end
277
+ end
278
+
279
+ W = Win32
280
+ private_constant :W
281
+
282
+ # Every *_fn method below memoizes a resolved Fiddle::Function keyed by
283
+ # the object's VTABLE address (W.vtable_address), not the object's own
284
+ # address: after a COM object is released, its heap address can be
285
+ # reused by an unrelated object with a different vtable, so keying by
286
+ # object address risks resolving a function pointer from the wrong
287
+ # vtable. Keying by vtable address is both correct (it's the actual
288
+ # implementation identity) and bounded (one entry per distinct
289
+ # implementation, not per live object).
290
+
291
+ def get_type_info_fn(idispatch_ptr)
292
+ @get_type_info_fns ||= {}
293
+ @get_type_info_fns[W.vtable_address(idispatch_ptr)] ||= W.vtable_function(
294
+ idispatch_ptr, 4, [W::VOIDP, W::DWORD, W::DWORD, W::VOIDP], W::LONG
295
+ )
296
+ end
297
+
298
+ def type_attr_fn(itypeinfo_ptr)
299
+ @type_attr_fns ||= {}
300
+ @type_attr_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
301
+ itypeinfo_ptr, ITYPEINFO_VTBL[:GetTypeAttr], [W::VOIDP, W::VOIDP], W::LONG
302
+ )
303
+ end
304
+
305
+ def func_desc_fn(itypeinfo_ptr)
306
+ @func_desc_fns ||= {}
307
+ @func_desc_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
308
+ itypeinfo_ptr, ITYPEINFO_VTBL[:GetFuncDesc], [W::VOIDP, W::DWORD, W::VOIDP], W::LONG
309
+ )
310
+ end
311
+
312
+ def var_desc_fn(itypeinfo_ptr)
313
+ @var_desc_fns ||= {}
314
+ @var_desc_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
315
+ itypeinfo_ptr, ITYPEINFO_VTBL[:GetVarDesc], [W::VOIDP, W::DWORD, W::VOIDP], W::LONG
316
+ )
317
+ end
318
+
319
+ def documentation_fn_for_typeinfo(itypeinfo_ptr)
320
+ @documentation_fns ||= {}
321
+ @documentation_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
322
+ itypeinfo_ptr, ITYPEINFO_VTBL[:GetDocumentation],
323
+ [W::VOIDP, W::LONG, W::VOIDP, W::VOIDP, W::VOIDP, W::VOIDP], W::LONG
324
+ )
325
+ end
326
+
327
+ def containing_typelib_fn(itypeinfo_ptr)
328
+ @containing_typelib_fns ||= {}
329
+ @containing_typelib_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
330
+ itypeinfo_ptr, ITYPEINFO_VTBL[:GetContainingTypeLib], [W::VOIDP, W::VOIDP, W::VOIDP], W::LONG
331
+ )
332
+ end
333
+
334
+ def release_type_attr_fn(itypeinfo_ptr)
335
+ @release_type_attr_fns ||= {}
336
+ @release_type_attr_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
337
+ itypeinfo_ptr, ITYPEINFO_VTBL[:ReleaseTypeAttr], [W::VOIDP, W::VOIDP], W::VOID
338
+ )
339
+ end
340
+
341
+ def release_func_desc_fn(itypeinfo_ptr)
342
+ @release_func_desc_fns ||= {}
343
+ @release_func_desc_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
344
+ itypeinfo_ptr, ITYPEINFO_VTBL[:ReleaseFuncDesc], [W::VOIDP, W::VOIDP], W::VOID
345
+ )
346
+ end
347
+
348
+ def release_var_desc_fn(itypeinfo_ptr)
349
+ @release_var_desc_fns ||= {}
350
+ @release_var_desc_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
351
+ itypeinfo_ptr, ITYPEINFO_VTBL[:ReleaseVarDesc], [W::VOIDP, W::VOIDP], W::VOID
352
+ )
353
+ end
354
+
355
+ def ref_type_info_fn(itypeinfo_ptr)
356
+ @ref_type_info_fns ||= {}
357
+ @ref_type_info_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
358
+ itypeinfo_ptr, ITYPEINFO_VTBL[:GetRefTypeInfo], [W::VOIDP, W::DWORD, W::VOIDP], W::LONG
359
+ )
360
+ end
361
+
362
+ def type_info_count_fn(itypelib_ptr)
363
+ @type_info_count_fns ||= {}
364
+ @type_info_count_fns[W.vtable_address(itypelib_ptr)] ||= W.vtable_function(
365
+ itypelib_ptr, ITYPELIB_VTBL[:GetTypeInfoCount], [W::VOIDP], W::DWORD
366
+ )
367
+ end
368
+
369
+ def type_info_fn(itypelib_ptr)
370
+ @type_info_fns ||= {}
371
+ @type_info_fns[W.vtable_address(itypelib_ptr)] ||= W.vtable_function(
372
+ itypelib_ptr, ITYPELIB_VTBL[:GetTypeInfo], [W::VOIDP, W::DWORD, W::VOIDP], W::LONG
373
+ )
374
+ end
375
+
376
+ def lib_attr_fn(itypelib_ptr)
377
+ @lib_attr_fns ||= {}
378
+ @lib_attr_fns[W.vtable_address(itypelib_ptr)] ||= W.vtable_function(
379
+ itypelib_ptr, ITYPELIB_VTBL[:GetLibAttr], [W::VOIDP, W::VOIDP], W::LONG
380
+ )
381
+ end
382
+
383
+ def documentation_fn_for_typelib(itypelib_ptr)
384
+ @typelib_documentation_fns ||= {}
385
+ @typelib_documentation_fns[W.vtable_address(itypelib_ptr)] ||= W.vtable_function(
386
+ itypelib_ptr, ITYPELIB_VTBL[:GetDocumentation],
387
+ [W::VOIDP, W::LONG, W::VOIDP, W::VOIDP, W::VOIDP, W::VOIDP], W::LONG
388
+ )
389
+ end
390
+
391
+ def release_tlib_attr_fn(itypelib_ptr)
392
+ @release_tlib_attr_fns ||= {}
393
+ @release_tlib_attr_fns[W.vtable_address(itypelib_ptr)] ||= W.vtable_function(
394
+ itypelib_ptr, ITYPELIB_VTBL[:ReleaseTLibAttr], [W::VOIDP, W::VOIDP], W::VOID
395
+ )
396
+ end
397
+
398
+ def impl_type_flags_fn(itypeinfo_ptr)
399
+ @impl_type_flags_fns ||= {}
400
+ @impl_type_flags_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
401
+ itypeinfo_ptr, ITYPEINFO_VTBL[:GetImplTypeFlags], [W::VOIDP, W::DWORD, W::VOIDP], W::LONG
402
+ )
403
+ end
404
+
405
+ def ref_type_of_impl_type_fn(itypeinfo_ptr)
406
+ @ref_type_of_impl_type_fns ||= {}
407
+ @ref_type_of_impl_type_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
408
+ itypeinfo_ptr, ITYPEINFO_VTBL[:GetRefTypeOfImplType], [W::VOIDP, W::DWORD, W::VOIDP], W::LONG
409
+ )
410
+ end
411
+
412
+ def get_names_fn(itypeinfo_ptr)
413
+ @get_names_fns ||= {}
414
+ @get_names_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
415
+ itypeinfo_ptr, ITYPEINFO_VTBL[:GetNames], [W::VOIDP, W::LONG, W::VOIDP, W::DWORD, W::VOIDP], W::LONG
416
+ )
417
+ end
418
+
419
+ def get_ids_of_names_fn(itypeinfo_ptr)
420
+ @get_ids_of_names_fns ||= {}
421
+ @get_ids_of_names_fns[W.vtable_address(itypeinfo_ptr)] ||= W.vtable_function(
422
+ itypeinfo_ptr, ITYPEINFO_VTBL[:GetIDsOfNames], [W::VOIDP, W::VOIDP, W::DWORD, W::VOIDP], W::LONG
423
+ )
424
+ end
425
+
426
+ def type_info_of_guid_fn(itypelib_ptr)
427
+ @type_info_of_guid_fns ||= {}
428
+ @type_info_of_guid_fns[W.vtable_address(itypelib_ptr)] ||= W.vtable_function(
429
+ itypelib_ptr, ITYPELIB_VTBL[:GetTypeInfoOfGuid], [W::VOIDP, W::VOIDP, W::VOIDP], W::LONG
430
+ )
431
+ end
432
+
433
+ HKEY_CLASSES_ROOT = 0x80000000
434
+ KEY_READ = 0x20019
435
+ REG_SZ = 1
436
+ REG_EXPAND_SZ = 2
437
+
438
+ def advapi32
439
+ @advapi32 ||= Fiddle.dlopen('advapi32')
440
+ end
441
+
442
+ def reg_open_key_ex
443
+ @reg_open_key_ex ||= Fiddle::Function.new(
444
+ advapi32['RegOpenKeyExW'], [W::VOIDP, W::VOIDP, W::DWORD, W::DWORD, W::VOIDP], W::LONG, W::STDCALL
445
+ )
446
+ end
447
+
448
+ def reg_query_value_ex
449
+ @reg_query_value_ex ||= Fiddle::Function.new(
450
+ advapi32['RegQueryValueExW'],
451
+ [W::VOIDP, W::VOIDP, W::VOIDP, W::VOIDP, W::VOIDP, W::VOIDP], W::LONG, W::STDCALL
452
+ )
453
+ end
454
+
455
+ def reg_close_key
456
+ @reg_close_key ||= Fiddle::Function.new(advapi32['RegCloseKey'], [W::VOIDP], W::LONG, W::STDCALL)
457
+ end
458
+
459
+ REG_ENUM_MAX_NAME = 256 # WCHARs; a registry key name is capped at 255 chars (MAX_KEY_LENGTH)
460
+
461
+ def reg_enum_key_ex
462
+ @reg_enum_key_ex ||= Fiddle::Function.new(
463
+ advapi32['RegEnumKeyExW'],
464
+ [W::VOIDP, W::DWORD, W::VOIDP, W::VOIDP, W::VOIDP, W::VOIDP, W::VOIDP, W::VOIDP], W::LONG, W::STDCALL
465
+ )
466
+ end
467
+
468
+ # Mirrors ext/win32ole/win32ole.c's reg_enum_key: returns the name of
469
+ # the index-th direct subkey of hkey, or nil once the index runs past
470
+ # the last one (RegEnumKeyExW returns ERROR_NO_MORE_ITEMS).
471
+ def reg_enum_key(hkey, index)
472
+ name_out = ("\x00" * (REG_ENUM_MAX_NAME * 2)).b
473
+ size_out = [REG_ENUM_MAX_NAME].pack('L')
474
+ err = reg_enum_key_ex.call(hkey, index, name_out, size_out, nil, nil, nil, nil)
475
+ return nil unless err.zero?
476
+
477
+ name_out[0, size_out.unpack1('L') * 2].force_encoding('UTF-16LE').encode('UTF-8')
478
+ end
479
+
480
+ REGKIND_NONE = 2
481
+
482
+ def load_type_lib_ex
483
+ @load_type_lib_ex ||= Fiddle::Function.new(
484
+ W.oleaut32['LoadTypeLibEx'], [W::VOIDP, W::LONG, W::VOIDP], W::LONG, W::STDCALL
485
+ )
486
+ end
487
+ end
488
+ end