unknownr 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/LICENSE +21 -0
  2. data/lib/unknownr.rb +276 -0
  3. metadata +79 -0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010 Radoslav Peev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/lib/unknownr.rb ADDED
@@ -0,0 +1,276 @@
1
+ require 'ffi'
2
+
3
+ module Unknownr
4
+ module Windows
5
+ extend FFI::Library
6
+
7
+ ffi_lib 'ole32'
8
+ ffi_convention :stdcall
9
+
10
+ S_OK = 0
11
+ S_FALSE = 1
12
+
13
+ E_UNEXPECTED = 0x8000FFFF - 0x1_0000_0000
14
+ E_NOTIMPL = 0x80004001 - 0x1_0000_0000
15
+ E_OUTOFMEMORY = 0x8007000E - 0x1_0000_0000
16
+ E_INVALIDARG = 0x80070057 - 0x1_0000_0000
17
+ E_NOINTERFACE = 0x80004002 - 0x1_0000_0000
18
+ E_POINTER = 0x80004003 - 0x1_0000_0000
19
+ E_HANDLE = 0x80070006 - 0x1_0000_0000
20
+ E_ABORT = 0x80004004 - 0x1_0000_0000
21
+ E_FAIL = 0x80004005 - 0x1_0000_0000
22
+ E_ACCESSDENIED = 0x80070005 - 0x1_0000_0000
23
+ E_PENDING = 0x8000000A - 0x1_0000_0000
24
+
25
+ FACILITY_WIN32 = 7
26
+
27
+ ERROR_CANCELLED = 1223
28
+
29
+ def SUCCEEDED(hr) hr >= 0 end
30
+ def FAILED(hr) hr < 0 end
31
+ def HRESULT_FROM_WIN32(x) (x <= 0) ? x : (x & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000 end
32
+
33
+ module_function \
34
+ :SUCCEEDED,
35
+ :FAILED,
36
+ :HRESULT_FROM_WIN32
37
+
38
+ def DetonateHresult(name, *args)
39
+ failed = FAILED(result = send(name, *args)) and raise "#{name} failed (hresult #{format('%#08x', result)})."
40
+
41
+ result
42
+ ensure
43
+ yield failed if block_given?
44
+ end
45
+
46
+ module_function :DetonateHresult
47
+
48
+ class GUID < FFI::Struct
49
+ layout \
50
+ :Data1, :ulong,
51
+ :Data2, :ushort,
52
+ :Data3, :ushort,
53
+ :Data4, [:uchar, 8]
54
+
55
+ def self.[](s)
56
+ raise 'Bad GUID format.' unless s =~ /^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/i
57
+
58
+ new.tap { |guid|
59
+ guid[:Data1] = s[0, 8].to_i(16)
60
+ guid[:Data2] = s[9, 4].to_i(16)
61
+ guid[:Data3] = s[14, 4].to_i(16)
62
+ guid[:Data4][0] = s[19, 2].to_i(16)
63
+ guid[:Data4][1] = s[21, 2].to_i(16)
64
+ s[24, 12].split('').each_slice(2).with_index { |a, i|
65
+ guid[:Data4][i + 2] = a.join('').to_i(16)
66
+ }
67
+ }
68
+ end
69
+ end
70
+
71
+ CLSCTX_INPROC_SERVER = 0x1
72
+ CLSCTX_INPROC_HANDLER = 0x2
73
+ CLSCTX_LOCAL_SERVER = 0x4
74
+ CLSCTX_INPROC_SERVER16 = 0x8
75
+ CLSCTX_REMOTE_SERVER = 0x10
76
+ CLSCTX_INPROC_HANDLER16 = 0x20
77
+ CLSCTX_RESERVED1 = 0x40
78
+ CLSCTX_RESERVED2 = 0x80
79
+ CLSCTX_RESERVED3 = 0x100
80
+ CLSCTX_RESERVED4 = 0x200
81
+ CLSCTX_NO_CODE_DOWNLOAD = 0x400
82
+ CLSCTX_RESERVED5 = 0x800
83
+ CLSCTX_NO_CUSTOM_MARSHAL = 0x1000
84
+ CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000
85
+ CLSCTX_NO_FAILURE_LOG = 0x4000
86
+ CLSCTX_DISABLE_AAA = 0x8000
87
+ CLSCTX_ENABLE_AAA = 0x10000
88
+ CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000
89
+ CLSCTX_ACTIVATE_32_BIT_SERVER = 0x40000
90
+ CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000
91
+ CLSCTX_ENABLE_CLOAKING = 0x100000
92
+ CLSCTX_PS_DLL = -0x80000000
93
+ CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER
94
+ CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
95
+ CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
96
+
97
+ attach_function :CoCreateInstance, [:pointer, :pointer, :ulong, :pointer, :pointer], :long
98
+
99
+ module COM
100
+ module Interface
101
+ def self.[](*args)
102
+ spec, iid, *ifaces = args.reverse
103
+
104
+ spec.each { |name, signature| signature[0].unshift(:pointer) }
105
+
106
+ Class.new(FFI::Struct) {
107
+ const_set(:IID, iid)
108
+
109
+ const_set(:VTBL, Class.new(FFI::Struct) {
110
+ const_set(:SPEC, Hash[(ifaces.map { |iface| iface::VTBL::SPEC.to_a } << spec.to_a).flatten(1)])
111
+
112
+ layout \
113
+ *self::SPEC.map { |name, signature| [name, callback(*signature)] }.flatten
114
+ })
115
+
116
+ layout \
117
+ :lpVtbl, :pointer
118
+ }
119
+ end
120
+ end
121
+
122
+ module Helpers
123
+ def QueryInstance(klass)
124
+ instance = nil
125
+
126
+ FFI::MemoryPointer.new(:pointer) { |ppv|
127
+ QueryInterface(klass::IID, ppv)
128
+
129
+ instance = klass.new(ppv.read_pointer)
130
+ }
131
+
132
+ begin
133
+ yield instance; return self
134
+ ensure
135
+ instance.Release
136
+ end if block_given?
137
+
138
+ instance
139
+ end
140
+
141
+ def UseInstance(klass, name, *args)
142
+ instance = nil
143
+
144
+ FFI::MemoryPointer.new(:pointer) { |ppv|
145
+ send(name, *args, klass::IID, ppv)
146
+
147
+ yield instance = klass.new(ppv.read_pointer)
148
+ }
149
+
150
+ self
151
+ ensure
152
+ instance.Release if instance
153
+ end
154
+ end
155
+
156
+ module Instance
157
+ def self.[](iface)
158
+ Class.new(iface) {
159
+ send(:include, Helpers)
160
+
161
+ def initialize(pointer)
162
+ self.pointer = pointer
163
+
164
+ @vtbl = self.class::VTBL.new(self[:lpVtbl])
165
+ end
166
+
167
+ attr_reader :vtbl
168
+
169
+ self::VTBL.members.each { |name|
170
+ define_method(name) { |*args|
171
+ raise "#{self}.#{name} failed." if Windows.FAILED(result = @vtbl[name].call(self, *args)); result
172
+ }
173
+ }
174
+ }
175
+ end
176
+ end
177
+
178
+ module Factory
179
+ def self.[](iface, clsid)
180
+ Class.new(iface) {
181
+ send(:include, Helpers)
182
+
183
+ const_set(:CLSID, clsid)
184
+
185
+ def initialize(opts = {})
186
+ @opts = opts
187
+
188
+ @opts[:clsctx] ||= CLSCTX_INPROC_SERVER
189
+
190
+ FFI::MemoryPointer.new(:pointer) { |ppv|
191
+ raise "CoCreateInstance failed (#{self.class})." if
192
+ Windows.FAILED(Windows.CoCreateInstance(self.class::CLSID, nil, @opts[:clsctx], self.class::IID, ppv))
193
+
194
+ self.pointer = ppv.read_pointer
195
+ }
196
+
197
+ @vtbl = self.class::VTBL.new(self[:lpVtbl])
198
+ end
199
+
200
+ attr_reader :vtbl
201
+
202
+ self::VTBL.members.each { |name|
203
+ define_method(name) { |*args|
204
+ raise "#{self}.#{name} failed." if Windows.FAILED(result = @vtbl[name].call(self, *args)); result
205
+ }
206
+ }
207
+ }
208
+ end
209
+ end
210
+
211
+ module Callback
212
+ def self.[](iface)
213
+ Class.new(iface) {
214
+ send(:include, Helpers)
215
+
216
+ def initialize(opts = {})
217
+ @vtbl, @refc = self.class::VTBL.new, 1
218
+
219
+ @vtbl.members.each { |name|
220
+ @vtbl[name] = instance_variable_set("@fn#{name}",
221
+ FFI::Function.new(*@vtbl.class::SPEC[name].reverse, convention: :stdcall) { |*args|
222
+ send(name, *args[1..-1])
223
+ }
224
+ )
225
+ }
226
+
227
+ self[:lpVtbl] = @vtbl
228
+
229
+ begin
230
+ yield self
231
+ ensure
232
+ Release()
233
+ end if block_given?
234
+ end
235
+
236
+ attr_reader :vtbl, :refc
237
+
238
+ def QueryInterface(riid, ppv)
239
+ if [IUnknown::IID, self.class::IID].any? { |iid| Windows.memcmp(riid, iid, iid.size) == 0 }
240
+ ppv.write_pointer(self)
241
+ else
242
+ ppv.write_pointer(0); return E_NOINTERFACE
243
+ end
244
+
245
+ AddRef(); S_OK
246
+ end
247
+
248
+ def AddRef
249
+ @refc += 1
250
+ end
251
+
252
+ def Release
253
+ @refc -= 1
254
+ end
255
+
256
+ (self::VTBL.members - IUnknown::VTBL.members).each { |name|
257
+ define_method(name) { |*args|
258
+ E_NOTIMPL
259
+ }
260
+ }
261
+ }
262
+ end
263
+ end
264
+ end
265
+
266
+ IUnknown = COM::Interface[
267
+ GUID['00000000-0000-0000-C000-000000000046'],
268
+
269
+ QueryInterface: [[:pointer, :pointer], :long],
270
+ AddRef: [[], :ulong],
271
+ Release: [[], :ulong]
272
+ ]
273
+
274
+ Unknown = COM::Instance[IUnknown]
275
+ end
276
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unknownr
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Radoslav Peev
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-07 00:00:00 +03:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: ffi
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ - 6
31
+ - 3
32
+ version: 0.6.3
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description:
36
+ email:
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - lib/unknownr.rb
45
+ - LICENSE
46
+ has_rdoc: true
47
+ homepage:
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.3.7
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: IUnknown COM objects support for Ruby-FFI on Windows
78
+ test_files: []
79
+