sleipnir-api 0.3.0 → 0.4.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.
@@ -0,0 +1,20 @@
1
+ require "sleipnir_api/ffi/base"
2
+
3
+ module SleipnirAPI::FFI
4
+ module Kernel32 #:nodoc:
5
+ extend SleipnirAPI::FFI::Base
6
+
7
+ define_ffi_entry(:GetLongPathName, "PPL", "L", "kernel32", "GetLongPathNameA")
8
+ define_ffi_entry(:RtlZeroMemory, "PL", "L", "kernel32")
9
+ define_ffi_entry(:GetLastError, "V", "L", "kernel32")
10
+
11
+ module_function
12
+
13
+ def get_long_path_name(short_path)
14
+ buf = "\0" * 1024
15
+ len = GetLongPathName(short_path, buf, buf.length)
16
+ buf[0...len]
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,246 @@
1
+ require "sleipnir_api/ffi/base"
2
+
3
+ module SleipnirAPI::FFI
4
+
5
+ class Type #:nodoc:
6
+ attr_reader :name
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+ end
11
+
12
+ class PrimitiveType < Type #:nodoc:
13
+ BASE_TYPES = {
14
+ :int8 => ["c", 1],
15
+ :uint8 => ["C", 1],
16
+ :int16 => ["s", 2],
17
+ :uint16 => ["S", 2],
18
+ :int32 => ["l", 4],
19
+ :uint32 => ["L", 4],
20
+ :float => ["f", 4],
21
+ :double => ["d", 8],
22
+ :string => ["p", 4],
23
+ :pointer => ["P", 4],
24
+ }
25
+
26
+ attr_reader :size, :format
27
+ def initialize(name, size=guess_size(name), format=guess_format(name))
28
+ super(name)
29
+ @size = size
30
+ @format = format
31
+ end
32
+
33
+ def pack(value)
34
+ [value].pack(format)
35
+ end
36
+
37
+ def guess_format(name)
38
+ BASE_TYPES[name][0]
39
+ end
40
+ private :guess_format
41
+
42
+ def guess_size(name)
43
+ BASE_TYPES[name][1]
44
+ end
45
+ private :guess_size
46
+ end
47
+
48
+ class StructureType < Type #:nodoc:
49
+ PACKING_ALIGN = 4
50
+
51
+ attr_reader :name, :size, :ruby_class, :fields
52
+ def initialize(name, size=nil)
53
+ super(name)
54
+ @ruby_class = nil
55
+ @fields = []
56
+ @size = size
57
+ end
58
+
59
+ def calc_offsets
60
+ offset = 0
61
+ @fields.each do |f|
62
+ align = [f.type.size, PACKING_ALIGN].min
63
+ offset = (offset.to_f / align).ceil * align
64
+ size = f.size
65
+ if f.offset.nil?
66
+ f.offset = offset
67
+ offset += size
68
+ else
69
+ offset = f.offset
70
+ offset += size
71
+ end
72
+ end
73
+ end
74
+
75
+ def calc_size
76
+ return @size if @size
77
+ if @fields.empty?
78
+ @size = 0
79
+ return
80
+ end
81
+ align = [@fields.first.type.size, PACKING_ALIGN].min
82
+ offset = @fields.last.offset + @fields.last.size
83
+ @size = (offset.to_f / align).ceil * align
84
+ end
85
+
86
+ def pack(value)
87
+ packed = @fields.zip(value.values).map{|f,v| [f.offset, f.pack(v)] }
88
+ r = ""
89
+ packed.each do |offset,c|
90
+ align!(r, offset)
91
+ r << c
92
+ end
93
+ align!(r, size)
94
+ r
95
+ end
96
+
97
+ def align!(r, offset)
98
+ if offset < r.length
99
+ raise "buffer over flow: expected offset #{offset}, but was #{r.length}"
100
+ end
101
+ if r.length < offset
102
+ r << "\0" * (offset - r.length)
103
+ end
104
+ end
105
+
106
+ def define!(ns)
107
+ calc_offsets
108
+ calc_size
109
+ define_ruby_class(ns)
110
+ end
111
+
112
+ def define_ruby_class(ns)
113
+ types = self
114
+ name = @name
115
+ clazz = Class.new
116
+ clazz.instance_eval {
117
+ types.fields.each {|f|
118
+ attr_accessor f.name
119
+ }
120
+ define_method(:initialize) {|*args|
121
+ types.fields.zip(args).each {|f,arg|
122
+ instance_variable_set(f.var, arg || f.init)
123
+ }
124
+ }
125
+ define_method(:pack) {
126
+ types.pack(self)
127
+ }
128
+ }
129
+ clazz.class_eval %Q{
130
+ def names
131
+ [#{types.fields.map{|f| ":#{f.name}"} * ','}]
132
+ end
133
+ def values
134
+ [#{types.fields.map{|f| f.var} * ','}]
135
+ end
136
+ }
137
+ (class <<clazz; self; end).instance_eval {
138
+ define_method(:packed_size) {
139
+ CStruct.sizeof(name)
140
+ }
141
+ define_method(:c_type) {
142
+ CStruct.c_type(name)
143
+ }
144
+ }
145
+ @ruby_class = clazz
146
+ ns.const_set(@name, clazz)
147
+ end
148
+ end
149
+
150
+ class Field #:nodoc:
151
+ attr_reader :type, :name, :count, :var
152
+ attr_accessor :offset
153
+ def initialize(type, name, opts=nil)
154
+ @type = type
155
+ @name = name
156
+ @var = "@#{name}".to_sym
157
+ @opts = opts || {}
158
+ @offset = @opts[:offset] || nil
159
+ @count = @opts[:dim] || 1
160
+ end
161
+
162
+ def array?
163
+ count > 0
164
+ end
165
+
166
+ def init
167
+ if array?
168
+ init_one
169
+ else
170
+ (0...count).map{ init_one }
171
+ end
172
+ end
173
+
174
+ def size
175
+ @type.size * count
176
+ end
177
+
178
+ def pack(value)
179
+ if count == 1
180
+ @type.pack(value)
181
+ else
182
+ raise "hoge" unless Array === value
183
+ raise "foo" unless value.length == count
184
+ value.map{|e| @type.pack(e) }.join
185
+ end
186
+ end
187
+
188
+ def init_one
189
+ if PrimitiveType === @type
190
+ 0
191
+ else
192
+ @type.ruby_class.new
193
+ end
194
+ end
195
+ private :init_one
196
+ end
197
+
198
+ module CStruct #:nodoc:
199
+ TYPES = PrimitiveType::BASE_TYPES.inject({}){|h,t|
200
+ h[t[0]] = PrimitiveType.new(t[0])
201
+ h
202
+ }
203
+
204
+ def define_c_struct(name, size=nil, &block)
205
+ define_c_struct_under(self, name, size, &block)
206
+ end
207
+
208
+ module_function
209
+
210
+ def define_c_struct_under(ns, name, size=nil, &block)
211
+ dsl = DSL.new(name, size)
212
+ dsl.instance_eval(&block)
213
+ dsl.struct.define!(ns)
214
+ TYPES[name] = dsl.struct
215
+ dsl.struct
216
+ end
217
+
218
+ def sizeof(type)
219
+ c_type(type).size
220
+ end
221
+
222
+ def c_type(type)
223
+ TYPES[type]
224
+ end
225
+
226
+ def define_c_type(type, decl)
227
+ TYPES[decl] = TYPES[type]
228
+ end
229
+
230
+ class DSL #:nodoc:
231
+ attr_reader :struct
232
+
233
+ def initialize(name, size)
234
+ @struct = StructureType.new(name, size)
235
+ end
236
+
237
+ def method_missing(name, *args)
238
+ type = CStruct.c_type(name)
239
+ raise "unknown type: #{name}" unless type
240
+ name = args.shift
241
+ @struct.fields << Field.new(type, name, *args)
242
+ end
243
+ end
244
+ end
245
+
246
+ end
@@ -0,0 +1,116 @@
1
+ require "sleipnir_api/ffi/base"
2
+
3
+ module SleipnirAPI::FFI
4
+ module User32 #:nodoc:
5
+ extend SleipnirAPI::FFI::Base
6
+
7
+ GW_HWNDFIRST = 0
8
+ GW_HWNDLAST = 1
9
+ GW_HWNDNEXT = 2
10
+ GW_HWNDPREV = 3
11
+
12
+ define_ffi_entry(:GetWindowThreadProcessId, "LP", "L", "user32")
13
+ define_ffi_entry(:GetDC, 'L', 'L', 'user32')
14
+ define_ffi_entry(:GetWindowDC, 'L', 'L', 'user32')
15
+ define_ffi_entry(:ReleaseDC, 'LL', 'I', 'user32')
16
+
17
+ define_ffi_entry(:OpenIcon, 'L', 'B', 'user32')
18
+ define_ffi_entry(:CloseWindow, 'L', 'B', 'user32')
19
+ define_ffi_entry(:BringWindowToTop, 'L', 'B', 'user32')
20
+ define_ffi_entry(:GetActiveWindow, '', 'L', 'user32')
21
+ define_ffi_entry(:GetForegroundWindow, '', 'L', 'user32')
22
+ define_ffi_entry(:GetTopWindow, 'L', 'L', 'user32')
23
+ define_ffi_entry(:GetWindow, 'LI', 'L', 'user32')
24
+ define_ffi_entry(:GetWindowRect, 'LP', 'B', 'user32')
25
+
26
+ module_function
27
+
28
+ def get_window_thread_process_id(hwnd)
29
+ n = "\0" * 4
30
+ th = GetWindowThreadProcessId.call(hwnd, n)
31
+ return [th, n.unpack("L")[0]]
32
+ end
33
+
34
+ def get_window_thread_id(hwnd)
35
+ get_window_thread_process_id(hwnd)[0]
36
+ end
37
+
38
+ def get_window_process_id(hwnd)
39
+ get_window_thread_process_id(hwnd)[1]
40
+ end
41
+
42
+ def with_window_dc(hwnd)
43
+ hdc = GetWindowDC(hwnd)
44
+ raise Win32APIError, "GetDC failed" if hdc.nil? or hdc.zero?
45
+ begin
46
+ yield hdc
47
+ ensure
48
+ ReleaseDC(hwnd, hdc)
49
+ end
50
+ end
51
+
52
+ def get_window_rect(hwnd)
53
+ buf = [0, 0, 0, 0].pack("L4")
54
+ GetWindowRect(hwnd, buf)
55
+ buf.unpack("L4")
56
+ end
57
+
58
+
59
+ def get_top_window(hwnd)
60
+ r = GetTopWindow(hwnd)
61
+ if r.nil? or r.zero?
62
+ nil
63
+ else
64
+ r
65
+ end
66
+ end
67
+
68
+ def get_window(hwnd, cmd)
69
+ r = GetWindow(hwnd, cmd)
70
+ if r.nil? or r.zero?
71
+ nil
72
+ else
73
+ r
74
+ end
75
+ end
76
+
77
+ def get_first_child(hwnd)
78
+ get_top_window(hwnd)
79
+ end
80
+
81
+ def get_first_sibling(hwnd)
82
+ get_window(hwnd, GW_HWNDFIRST)
83
+ end
84
+
85
+ def get_last_sibling(hwnd)
86
+ get_window(hwnd, GW_HWNDLAST)
87
+ end
88
+
89
+ def get_next_sibling(hwnd)
90
+ get_window(hwnd, GW_HWNDNEXT)
91
+ end
92
+
93
+ def get_prev_sibling(hwnd)
94
+ get_window(hwnd, GW_HWNDPREV)
95
+ end
96
+
97
+ def each_child_window(hwnd)
98
+ child = get_first_child(hwnd)
99
+ if child
100
+ yield child
101
+ while child = get_next_sibling(child)
102
+ yield child
103
+ end
104
+ end
105
+ end
106
+
107
+ def list_window(hwnd)
108
+ r = [hwnd]
109
+ each_child_window(hwnd) {|child|
110
+ r << list_window(child)
111
+ }
112
+ r
113
+ end
114
+
115
+ end
116
+ end
@@ -6,8 +6,9 @@ require "sleipnir_api/registry"
6
6
  module SleipnirAPI
7
7
  class Process # :nodoc:
8
8
  class <<self
9
-
10
9
  include SleipnirAPI::Registry
10
+ include SleipnirAPI::FFI::User32
11
+ include SleipnirAPI::FFI::Kernel32
11
12
 
12
13
  @@wbem = nil
13
14
 
@@ -37,13 +38,15 @@ module SleipnirAPI
37
38
  end
38
39
 
39
40
  def raise_create_error(msg)
40
- raise Sleipnir::ProcessError, "Failed to create Sleipnir process: #{msg}."
41
+ raise SleipnirAPI::ProcessError, "Failed to create Sleipnir process: #{msg}."
41
42
  end
42
43
 
43
44
  def terminate!(handle)
45
+ proc_id = get_window_process_id(handle)
44
46
  query_sleipnir.each do |obj|
45
- obj.Terminate
47
+ return obj.Terminate if obj.ProcessId == proc_id
46
48
  end
49
+ nil
47
50
  end
48
51
 
49
52
  def query_sleipnir
@@ -68,7 +68,10 @@ module SleipnirAPI
68
68
  include DataUtil
69
69
 
70
70
  # SleipnirAPI::Sleipnir object
71
- attr_reader :sleipnir, :default_opts
71
+ attr_reader :sleipnir
72
+
73
+ # default options
74
+ attr_reader :default_opts
72
75
 
73
76
  def initialize(sleipnir, default_opts = nil)
74
77
  @sleipnir = sleipnir
@@ -262,7 +265,7 @@ module SleipnirAPI
262
265
  expand_user_path("script.ini")
263
266
  end
264
267
 
265
- # 指定された ini ファイルを操作する Sleipnir::Profile::Ini オブジェクトを返します。
268
+ # 指定された ini ファイルを操作する SleipnirAPI::Profile::Ini オブジェクトを返します。
266
269
  #
267
270
  # pnir = SleipnirAPI.connect
268
271
  # proxy = pnir.profile.ini("Proxy.ini", :default => 123)
@@ -273,7 +276,7 @@ module SleipnirAPI
273
276
  Ini.new(self, str(name), options(opts))
274
277
  end
275
278
 
276
- # メソッド名を ini ファイル名とみなして Sleipnir::Profile::Ini オブジェクトを返します。
279
+ # メソッド名を ini ファイル名とみなして SleipnirAPI::Profile::Ini オブジェクトを返します。
277
280
  #
278
281
  # pnir = SleipnirAPI.connect
279
282
  # proxy = pnir.profile.Proxy(:default => 123)
@@ -147,7 +147,7 @@ module SleipnirAPI
147
147
  not script_ini?
148
148
  end
149
149
 
150
- # 指定されたセクションを操作する Sleipnir::Profile::Section オブジェクトを返します。
150
+ # 指定されたセクションを操作する SleipnirAPI::Profile::Section オブジェクトを返します。
151
151
  #
152
152
  # pnir = SleipnirAPI.connect
153
153
  # proxy = pnir.profile.ini("Proxy.ini", :default => 123)
@@ -159,7 +159,7 @@ module SleipnirAPI
159
159
  Section.new(self, str(name), options(opts))
160
160
  end
161
161
 
162
- # メソッド名をセクション名とみなして Sleipnir::Profile::Section オブジェクトを返します。
162
+ # メソッド名をセクション名とみなして SleipnirAPI::Profile::Section オブジェクトを返します。
163
163
  #
164
164
  # pnir = SleipnirAPI.connect
165
165
  # proxy = pnir.profile.Proxy(:default => 123)