sleipnir-api 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)