win 0.1.13 → 0.1.16
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/win/dde.rb +13 -13
- data/lib/win/error.rb +1066 -0
- data/lib/win/extensions.rb +14 -1
- data/lib/win/gui/input.rb +24 -0
- data/lib/win/gui/window.rb +88 -25
- data/lib/win/library.rb +39 -27
- data/spec/spec_helper.rb +19 -9
- data/spec/win/dde_spec.rb +2 -1
- data/spec/win/error_spec.rb +53 -0
- data/spec/win/extensions_spec.rb +12 -2
- data/spec/win/gui/input_spec.rb +7 -7
- data/spec/win/gui/window_spec.rb +414 -348
- data/win.gemspec +5 -5
- metadata +5 -5
- data/lib/win/gui/window/window.rb +0 -88
- data/spec/win/gui/window/window_spec.rb +0 -129
data/lib/win/extensions.rb
CHANGED
@@ -3,7 +3,20 @@ class String
|
|
3
3
|
def snake_case
|
4
4
|
gsub(/([a-z])([A-Z0-9])/, '\1_\2' ).downcase
|
5
5
|
end
|
6
|
-
|
6
|
+
|
7
|
+
# returns camel_case representation of string
|
8
|
+
def camel_case
|
9
|
+
if self.include? '_'
|
10
|
+
self.split('_').map{|e| e.capitalize}.join
|
11
|
+
else
|
12
|
+
unless self =~ (/^[A-Z]/)
|
13
|
+
self.capitalize
|
14
|
+
else
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
7
20
|
# converts string to 'wide char' (Windows Unicode) format
|
8
21
|
def to_w
|
9
22
|
(self+"\x00").encode('utf-16LE')
|
data/lib/win/gui/input.rb
CHANGED
@@ -240,6 +240,30 @@ module Win
|
|
240
240
|
#
|
241
241
|
function :mouse_event, [:ulong, :ulong, :ulong, :ulong, :ulong, ], :void
|
242
242
|
|
243
|
+
##
|
244
|
+
# GetCursorPos Function retrieves the cursor's position, in screen coordinates.
|
245
|
+
#
|
246
|
+
# [*Syntax*]: BOOL GetCursorPos( LPPOINT lpPoint );
|
247
|
+
# Parameters
|
248
|
+
#
|
249
|
+
# lpPoint
|
250
|
+
# [out] Pointer to a POINT structure that receives the screen coordinates of the cursor.
|
251
|
+
# Return Value
|
252
|
+
#
|
253
|
+
# Returns nonzero if successful or zero otherwise. To get extended error information, call GetLastError.
|
254
|
+
#
|
255
|
+
#
|
256
|
+
#
|
257
|
+
#
|
258
|
+
# Remarks
|
259
|
+
#
|
260
|
+
# The cursor position is always specified in screen coordinates and is not affected by the mapping mode of the window that contains the cursor.
|
261
|
+
#
|
262
|
+
# The calling process must have WINSTA_READATTRIBUTES access to the window station.
|
263
|
+
#
|
264
|
+
# The input desktop must be the current desktop when you call GetCursorPos. Call OpenInputDesktop to determine whether the current desktop is the input desktop. If it is not, call SetThreadDesktop with the HDESK returned by OpenInputDesktop to switch to that desktop.
|
265
|
+
|
266
|
+
|
243
267
|
##
|
244
268
|
# SetCursorPos Function moves the cursor to the specified screen coordinates. If the new coordinates are not
|
245
269
|
# within the screen rectangle set by the most recent ClipCursor function call, the system automatically adjusts
|
data/lib/win/gui/window.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'win/library'
|
2
|
+
require 'win/gui/message' # needed because some convenience methods work via PostMessage
|
2
3
|
|
3
4
|
module Win
|
4
5
|
module GUI
|
@@ -106,7 +107,7 @@ module Win
|
|
106
107
|
# :call-seq:
|
107
108
|
# window?( win_handle )
|
108
109
|
#
|
109
|
-
function :IsWindow, [:
|
110
|
+
function :IsWindow, [:HWND], :int, boolean: true
|
110
111
|
|
111
112
|
##
|
112
113
|
# The IsWindowVisible function retrieves the visibility state of the specified window.
|
@@ -129,7 +130,7 @@ module Win
|
|
129
130
|
# :call-seq:
|
130
131
|
# [window_]visible?( win_handle )
|
131
132
|
#
|
132
|
-
function :IsWindowVisible, [:
|
133
|
+
function :IsWindowVisible, [:HWND], :int, boolean: true, aliases: :visible?
|
133
134
|
|
134
135
|
##
|
135
136
|
# Tests whether the specified window is maximized.
|
@@ -143,7 +144,7 @@ module Win
|
|
143
144
|
# :call-seq:
|
144
145
|
# zoomed?( win_handle ), maximized?( win_handle )
|
145
146
|
#
|
146
|
-
function :IsZoomed, [:
|
147
|
+
function :IsZoomed, [:HWND], :int, boolean: true, aliases: :maximized?
|
147
148
|
|
148
149
|
##
|
149
150
|
# Tests whether the specified window is minimized.
|
@@ -157,7 +158,7 @@ module Win
|
|
157
158
|
# :call-seq:
|
158
159
|
# iconic?( win_handle ), minimized?( win_handle )
|
159
160
|
#
|
160
|
-
function :IsIconic, [:
|
161
|
+
function :IsIconic, [:HWND], :int, boolean: true, aliases: :minimized?
|
161
162
|
|
162
163
|
##
|
163
164
|
# Tests whether a window is a child (or descendant) window of a specified parent window.
|
@@ -176,7 +177,7 @@ module Win
|
|
176
177
|
# :call-seq:
|
177
178
|
# child?( win_handle )
|
178
179
|
#
|
179
|
-
function :IsChild, [:
|
180
|
+
function :IsChild, [:HWND, :HWND], :int, boolean: true
|
180
181
|
|
181
182
|
##
|
182
183
|
# The FindWindow function retrieves a handle to the top-level window whose class name and window name
|
@@ -214,7 +215,7 @@ module Win
|
|
214
215
|
# :call-seq:
|
215
216
|
# win_handle = find_window( class_name, win_name )
|
216
217
|
#
|
217
|
-
function :FindWindow, [:pointer, :pointer], :
|
218
|
+
function :FindWindow, [:pointer, :pointer], :HWND, zeronil: true
|
218
219
|
|
219
220
|
##
|
220
221
|
# Unicode version of FindWindow (strings must be encoded as utf-16LE AND terminate with "\x00\x00")
|
@@ -222,7 +223,7 @@ module Win
|
|
222
223
|
# :call-seq:
|
223
224
|
# win_handle = find_window_w( class_name, win_name )
|
224
225
|
#
|
225
|
-
function :FindWindowW, [:pointer, :pointer], :
|
226
|
+
function :FindWindowW, [:pointer, :pointer], :HWND, zeronil: true
|
226
227
|
|
227
228
|
##
|
228
229
|
# The FindWindowEx function retrieves a handle to a window whose class name and window name match the specified
|
@@ -265,7 +266,7 @@ module Win
|
|
265
266
|
# :call-seq:
|
266
267
|
# win_handle = find_window_ex( win_handle, after_child, class_name, win_name )
|
267
268
|
#
|
268
|
-
function :FindWindowEx, [:
|
269
|
+
function :FindWindowEx, [:HWND, :HWND, :pointer, :pointer], :HWND, zeronil: true
|
269
270
|
|
270
271
|
##
|
271
272
|
# GetWindowText returns the text of the specified window's title bar (if it has one).
|
@@ -305,7 +306,7 @@ module Win
|
|
305
306
|
#:call-seq:
|
306
307
|
# text = [get_]window_text( win_handle )
|
307
308
|
#
|
308
|
-
function :GetWindowText, [:
|
309
|
+
function :GetWindowText, [:HWND, :pointer, :int], :int, &return_string
|
309
310
|
|
310
311
|
##
|
311
312
|
# GetWindowTextW is a Unicode version of GetWindowText (returns rstripped utf-8 string)
|
@@ -314,7 +315,7 @@ module Win
|
|
314
315
|
#:call-seq:
|
315
316
|
# text = [get_]window_text_w( win_handle )
|
316
317
|
#
|
317
|
-
function :GetWindowTextW, [:
|
318
|
+
function :GetWindowTextW, [:HWND, :pointer, :int], :int, &return_string('utf-8')
|
318
319
|
|
319
320
|
##
|
320
321
|
# GetClassName retrieves the name of the class to which the specified window belongs.
|
@@ -337,7 +338,7 @@ module Win
|
|
337
338
|
#:call-seq:
|
338
339
|
# text = [get_]class_name( win_handle )
|
339
340
|
#
|
340
|
-
function :GetClassName, [:
|
341
|
+
function :GetClassName, [:HWND, :pointer, :int], :int, &return_string
|
341
342
|
|
342
343
|
##
|
343
344
|
# GetClassNameW is a Unicode version of GetClassName (returns rstripped utf-8 string)
|
@@ -346,7 +347,7 @@ module Win
|
|
346
347
|
#:call-seq:
|
347
348
|
# text = [get_]class_name_w( win_handle )
|
348
349
|
#
|
349
|
-
function :GetClassNameW, [:
|
350
|
+
function :GetClassNameW, [:HWND, :pointer, :int], :int, &return_string('utf-8')
|
350
351
|
|
351
352
|
##
|
352
353
|
# ShowWindow shows and hides windows (sets the specified window's show state).
|
@@ -367,9 +368,51 @@ module Win
|
|
367
368
|
#:call-seq:
|
368
369
|
# was_visible = show_window( win_handle, cmd )
|
369
370
|
#
|
370
|
-
function :ShowWindow, [:
|
371
|
+
function :ShowWindow, [:HWND, :int], :int, boolean: true,
|
371
372
|
&->(api, handle, cmd=SW_SHOW) { api.call handle, cmd }
|
372
373
|
|
374
|
+
##
|
375
|
+
# The CloseWindow function minimizes (but does not destroy) the specified window.
|
376
|
+
#
|
377
|
+
# [*Syntax*]: BOOL CloseWindow( HWND hWnd );
|
378
|
+
#
|
379
|
+
# hWnd:: [in] Handle to the window to be minimized.
|
380
|
+
#
|
381
|
+
# *Returns*:: If the function succeeds, the return value is nonzero (*true* in snake_case method). If the function
|
382
|
+
# fails, the return value is zero (*false). To get extended error information, call GetLastError.
|
383
|
+
# ---
|
384
|
+
# *Remarks*:
|
385
|
+
# To destroy a window, an application must use the DestroyWindow function.
|
386
|
+
#
|
387
|
+
function :CloseWindow, [:HWND], :int, boolean: true
|
388
|
+
|
389
|
+
##
|
390
|
+
# DestroyWindow function destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages
|
391
|
+
# to the window to deactivate it and remove the keyboard focus from it. The function also destroys the window's
|
392
|
+
# menu, flushes the thread message queue, destroys timers, removes clipboard ownership, and breaks the clipboard
|
393
|
+
# viewer chain (if the window is at the top of the viewer chain).
|
394
|
+
#
|
395
|
+
# If the specified window is a parent or owner window, DestroyWindow automatically destroys the associated child
|
396
|
+
# or owned windows when it destroys the parent or owner window. The function first destroys child or owned
|
397
|
+
# windows, and then it destroys the parent or owner window.
|
398
|
+
#
|
399
|
+
# DestroyWindow also destroys modeless dialog boxes created by the CreateDialog function.
|
400
|
+
#
|
401
|
+
# [*Syntax*]: BOOL DestroyWindow( HWND hWnd );
|
402
|
+
#
|
403
|
+
# hWnd:: [in] Handle to the window to be destroyed.
|
404
|
+
#
|
405
|
+
# *Returns*:: If the function succeeds, the return value is nonzero (snake_case method: *true*). If the function
|
406
|
+
# fails, the return value is zero (*false*). To get extended error information, call GetLastError.
|
407
|
+
# ---
|
408
|
+
# *Remarks*:
|
409
|
+
# A thread <b>cannot use DestroyWindow to destroy a window created by a different thread.</b> Use a convenience
|
410
|
+
# method destroy_unowned_window instead (it relies on
|
411
|
+
# If the window being destroyed is a child window that does not have the WS_EX_NOPARENTNOTIFY style, a
|
412
|
+
# WM_PARENTNOTIFY message is sent to the parent.
|
413
|
+
#
|
414
|
+
function :DestroyWindow, [:HWND], :int, boolean: true
|
415
|
+
|
373
416
|
##
|
374
417
|
# GetWindowThreadProcessId retrieves the identifier of the thread that created the specified window
|
375
418
|
# and, optionally, the identifier of the process that created the window.
|
@@ -392,11 +435,10 @@ module Win
|
|
392
435
|
#:call-seq:
|
393
436
|
# thread, process_id = [get_]window_tread_process_id( win_handle )
|
394
437
|
#
|
395
|
-
function :GetWindowThreadProcessId, [:
|
396
|
-
&->(api,
|
397
|
-
namespace.enforce_count( args, api.prototype, -1)
|
438
|
+
function :GetWindowThreadProcessId, [:HWND, :pointer], :long,
|
439
|
+
&->(api, handle) {
|
398
440
|
process = FFI::MemoryPointer.new(:long).write_long(1)
|
399
|
-
thread = api.call(
|
441
|
+
thread = api.call(handle, process)
|
400
442
|
thread == 0 ? [nil, nil] : [thread, process.read_long()] }
|
401
443
|
# weird lambda literal instead of normal block is needed because current version of RDoc
|
402
444
|
# goes crazy if block is attached to meta-definition
|
@@ -425,12 +467,11 @@ module Win
|
|
425
467
|
#:call-seq:
|
426
468
|
# rect = [get_]window_rect( win_handle )
|
427
469
|
#
|
428
|
-
function :GetWindowRect, [:
|
429
|
-
&->(api,
|
430
|
-
namespace.enforce_count( args, api.prototype, -1)
|
470
|
+
function :GetWindowRect, [:HWND, :pointer], :int,
|
471
|
+
&->(api, handle) {
|
431
472
|
rect = FFI::MemoryPointer.new(:long, 4)
|
432
473
|
#rect.write_array_of_long([0, 0, 0, 0])
|
433
|
-
res = api.call
|
474
|
+
res = api.call handle, rect
|
434
475
|
res == 0 ? [nil, nil, nil, nil] : rect.read_array_of_long(4) }
|
435
476
|
# weird lambda literal instead of normal block is needed because current version of RDoc
|
436
477
|
# goes crazy if block is attached to meta-definition
|
@@ -454,7 +495,7 @@ module Win
|
|
454
495
|
# :call-seq:
|
455
496
|
# EnumWindowsProc callback block: {|win_handle, value| your callback code }
|
456
497
|
#
|
457
|
-
callback :EnumWindowsProc, [:
|
498
|
+
callback :EnumWindowsProc, [:HWND, :long], :bool
|
458
499
|
|
459
500
|
##
|
460
501
|
# The EnumWindows function enumerates all top-level windows on the screen by passing the handle to
|
@@ -561,7 +602,7 @@ module Win
|
|
561
602
|
#:call-seq:
|
562
603
|
# handles = enum_child_windows( parent_handle, [value=0] ) {|handle, value| your callback procedure }
|
563
604
|
#
|
564
|
-
function :EnumChildWindows, [:
|
605
|
+
function :EnumChildWindows, [:HWND, :EnumWindowsProc, :long], :bool, &return_enum
|
565
606
|
|
566
607
|
##
|
567
608
|
# GetForegroundWindow function returns a handle to the foreground window (the window with which the user
|
@@ -576,7 +617,7 @@ module Win
|
|
576
617
|
#:call-seq:
|
577
618
|
# win_handle = [get_]foreground_window()
|
578
619
|
#
|
579
|
-
function :GetForegroundWindow, [], :
|
620
|
+
function :GetForegroundWindow, [], :HWND, zeronil: true
|
580
621
|
|
581
622
|
##
|
582
623
|
# The GetActiveWindow function retrieves the window handle to the active window attached to
|
@@ -593,7 +634,8 @@ module Win
|
|
593
634
|
#:call-seq:
|
594
635
|
# win_handle = [get_]active_window()
|
595
636
|
#
|
596
|
-
function :GetActiveWindow, [], :
|
637
|
+
function :GetActiveWindow, [], :HWND, zeronil: true
|
638
|
+
|
597
639
|
|
598
640
|
# Convenience wrapper methods:
|
599
641
|
|
@@ -610,6 +652,27 @@ module Win
|
|
610
652
|
def foreground?(win_handle)
|
611
653
|
win_handle == foreground_window
|
612
654
|
end
|
655
|
+
|
656
|
+
##
|
657
|
+
# Shuts down the window <b>created by different thread</b> by posting WM_SYSCOMMAND, SC_CLOSE message to it.
|
658
|
+
# This closely emulates user clicking on X button of the target window. As it would be expected, this
|
659
|
+
# actually gives the target window chance to close gracefully (it may ask user to save data and stuff).
|
660
|
+
# I have not find so far how to REALLY destroy window in different thread without it asking user anything.
|
661
|
+
#
|
662
|
+
def shut_window( win_handle)
|
663
|
+
post_message(win_handle, Win::GUI::Message::WM_SYSCOMMAND, Win::GUI::Message::SC_CLOSE, 0)
|
664
|
+
end
|
665
|
+
|
666
|
+
##
|
667
|
+
# Returns text associated with window by sending WM_GETTEXT message to it.
|
668
|
+
# ---
|
669
|
+
# *Remarks*: It is *different* from GetWindowText that returns only window title
|
670
|
+
#
|
671
|
+
def text( win_handle)
|
672
|
+
buffer = FFI::MemoryPointer.new :char, 1024
|
673
|
+
num_chars = send_message win_handle, Win::GUI::Message::WM_GETTEXT, buffer.size, buffer
|
674
|
+
num_chars == 0 ? nil : buffer.get_bytes(0, num_chars)
|
675
|
+
end
|
613
676
|
end
|
614
677
|
end
|
615
678
|
end
|
data/lib/win/library.rb
CHANGED
@@ -104,8 +104,10 @@ module Win
|
|
104
104
|
# :buffer_out – Similar to :pointer, but optimized for Buffers that the function can only write (not read).
|
105
105
|
# :buffer_inout – Similar to :pointer, but may be optimized for Buffers.
|
106
106
|
# :varargs – Variable arguments
|
107
|
+
# :enum - Enumerable type (should be defined)
|
108
|
+
# :char_array - ??
|
107
109
|
|
108
|
-
# Windows-specific
|
110
|
+
# Windows-specific type defs (ms-help://MS.MSDNQTR.v90.en/winprog/winprog/windows_data_types.htm):
|
109
111
|
ATOM: :ushort, # Atom ~= Symbol: Atom table stores strings and corresponding identifiers. Application
|
110
112
|
# places a string in an atom table and receives a 16-bit integer, called an atom, that
|
111
113
|
# can be used to access the string. Placed string is called an atom name.
|
@@ -128,6 +130,7 @@ module Win
|
|
128
130
|
HACCEL: :ulong, # (L) Handle to an accelerator table. WinDef.h: #typedef HANDLE HACCEL;
|
129
131
|
# See http://msdn.microsoft.com/en-us/library/ms645526%28VS.85%29.aspx
|
130
132
|
HANDLE: :ulong, # (L) Handle to an object. WinNT.h: #typedef PVOID HANDLE;
|
133
|
+
# todo: Platform-dependent! Need to change to :uint64 for Win64
|
131
134
|
HBITMAP: :ulong, # (L) Handle to a bitmap: http://msdn.microsoft.com/en-us/library/dd183377%28VS.85%29.aspx
|
132
135
|
HBRUSH: :ulong, # (L) Handle to a brush. http://msdn.microsoft.com/en-us/library/dd183394%28VS.85%29.aspx
|
133
136
|
HCOLORSPACE: :ulong, # (L) Handle to a color space. http://msdn.microsoft.com/en-us/library/ms536546%28VS.85%29.aspx
|
@@ -177,7 +180,7 @@ module Win
|
|
177
180
|
LCTYPE: :uint32, # Locale information type. For a list, see Locale Information Constants.
|
178
181
|
LGRPID: :uint32, # Language group identifier. For a list, see EnumLanguageGroupLocales.
|
179
182
|
LONG: :long, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
|
180
|
-
LONG32: :
|
183
|
+
LONG32: :int32, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
|
181
184
|
LONG64: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
|
182
185
|
LONGLONG: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
|
183
186
|
LONG_PTR: :long, # Signed long type for pointer precision. Use when casting a pointer to a long to
|
@@ -319,7 +322,7 @@ module Win
|
|
319
322
|
# :zeronil:: Forces method to return nil if function result is zero
|
320
323
|
#
|
321
324
|
def function(name, params, returns, options={}, &def_block)
|
322
|
-
|
325
|
+
snake_name, effective_names, aliases = generate_names(name, options)
|
323
326
|
params, returns = generate_signature(params, returns)
|
324
327
|
libs = ffi_libraries.map(&:name)
|
325
328
|
boolean = options[:boolean]
|
@@ -339,27 +342,35 @@ module Win
|
|
339
342
|
# Create API object that holds information about function names, params, etc
|
340
343
|
api = API.new(namespace, name, effective_name, params, returns, libs)
|
341
344
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
345
|
+
# Only define enhanced API if snake_name is different from original name (e.g. keybd_event)
|
346
|
+
unless snake_name.to_s == name.to_s
|
347
|
+
method_body = if def_block
|
348
|
+
if zeronil
|
349
|
+
->(*args, &block){ (res = def_block.(api, *args, &block)) != 0 ? res : nil }
|
350
|
+
elsif boolean
|
351
|
+
->(*args, &block){ def_block.(api, *args, &block) != 0 }
|
352
|
+
else
|
353
|
+
->(*args, &block){ def_block.(api, *args, &block) }
|
354
|
+
end
|
347
355
|
else
|
348
|
-
|
356
|
+
if zeronil
|
357
|
+
->(*args, &block){ (res = block ? block[api[*args]] : api[*args]) != 0 ? res : nil }
|
358
|
+
elsif boolean
|
359
|
+
->(*args, &block){ block ? block[api[*args]] : api[*args] != 0 }
|
360
|
+
else
|
361
|
+
->(*args, &block){ block ? block[api[*args]] : api[*args] }
|
362
|
+
end
|
349
363
|
end
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
->(*args, &block){ block ? block[api[*args]] : api[*args] }
|
364
|
+
|
365
|
+
define_method snake_name, &method_body # define snake_case instance method
|
366
|
+
|
367
|
+
eigenklass = class << self; self; end # Extracting eigenclass
|
368
|
+
eigenklass.class_eval do
|
369
|
+
define_method snake_name, &method_body # define snake_case class method
|
357
370
|
end
|
358
371
|
end
|
359
372
|
|
360
|
-
|
361
|
-
|
362
|
-
aliases.each {|ali| alias_method ali, method_name } # define aliases
|
373
|
+
aliases.each {|ali| alias_method ali, snake_name } # define aliases
|
363
374
|
api #return api object from function declaration
|
364
375
|
end
|
365
376
|
|
@@ -372,16 +383,16 @@ module Win
|
|
372
383
|
effective_names = [name]
|
373
384
|
effective_names += ["#{name}A", "#{name}W"] unless name =~ /[WA]$/
|
374
385
|
aliases = ([options[:alias]] + [options[:aliases]]).flatten.compact
|
375
|
-
|
376
|
-
case
|
386
|
+
snake_name = options[:rename] || name.snake_case
|
387
|
+
case snake_name
|
377
388
|
when /^is_/
|
378
|
-
aliases <<
|
389
|
+
aliases << snake_name.sub(/^is_/, '') + '?'
|
379
390
|
when /^set_/
|
380
|
-
aliases <<
|
391
|
+
aliases << snake_name.sub(/^set_/, '')+ '='
|
381
392
|
when /^get_/
|
382
|
-
aliases <<
|
393
|
+
aliases << snake_name.sub(/^get_/, '')
|
383
394
|
end
|
384
|
-
[
|
395
|
+
[snake_name, effective_names, aliases]
|
385
396
|
end
|
386
397
|
|
387
398
|
##
|
@@ -432,9 +443,10 @@ module Win
|
|
432
443
|
eigenklass.class_eval do
|
433
444
|
define_method(:namespace) {klass} # Defining new class method for host pointing to itself
|
434
445
|
alias_method :attach_callback, :callback
|
446
|
+
|
447
|
+
include ClassMethods
|
435
448
|
end
|
436
|
-
|
437
|
-
klass.extend ClassMethods
|
449
|
+
|
438
450
|
klass.ffi_lib 'user32', 'kernel32' # Default libraries
|
439
451
|
klass.ffi_convention :stdcall
|
440
452
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -74,6 +74,10 @@ module WinTest
|
|
74
74
|
def not_a_handle
|
75
75
|
123
|
76
76
|
end
|
77
|
+
|
78
|
+
def buffer
|
79
|
+
FFI::MemoryPointer.new(:char, 1024)
|
80
|
+
end
|
77
81
|
end
|
78
82
|
|
79
83
|
module WinTestApp
|
@@ -86,15 +90,21 @@ module WinTestApp
|
|
86
90
|
system TEST_APP_START
|
87
91
|
sleep TEST_SLEEP_DELAY until (handle = find_window(nil, TEST_WIN_TITLE))
|
88
92
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
+
textarea = find_window_ex(handle, 0, TEST_TEXTAREA_CLASS, nil)
|
94
|
+
app = "Locknote" # App identifier
|
95
|
+
|
96
|
+
eigenklass = class << app; self; end # Extracting app's eigenclass
|
97
|
+
eigenklass.class_eval do # Defining singleton methods on app
|
98
|
+
define_method(:handle) {handle}
|
99
|
+
define_method(:textarea) {textarea}
|
100
|
+
end
|
101
|
+
|
102
|
+
@launched_test_app = app
|
93
103
|
end
|
94
104
|
|
95
105
|
def close_test_app(app = @launched_test_app)
|
96
|
-
while app
|
97
|
-
|
106
|
+
while app && app.respond_to?( :handle) && find_window(nil, TEST_WIN_TITLE)
|
107
|
+
shut_window app.handle
|
98
108
|
sleep TEST_SLEEP_DELAY
|
99
109
|
end
|
100
110
|
@launched_test_app = nil
|
@@ -104,9 +114,9 @@ module WinTestApp
|
|
104
114
|
def test_app
|
105
115
|
app = launch_test_app
|
106
116
|
|
107
|
-
def app.textarea #define singleton method retrieving app's text area
|
108
|
-
Window::Window.new find_window_ex(self.handle, 0, TEST_TEXTAREA_CLASS, nil)
|
109
|
-
end
|
117
|
+
# def app.textarea #define singleton method retrieving app's text area
|
118
|
+
# Window::Window.new find_window_ex(self.handle, 0, TEST_TEXTAREA_CLASS, nil)
|
119
|
+
# end
|
110
120
|
|
111
121
|
yield app
|
112
122
|
close_test_app app
|
data/spec/win/dde_spec.rb
CHANGED
@@ -233,7 +233,8 @@ module WinDDETest
|
|
233
233
|
describe '#dde_connect' do
|
234
234
|
spec{ use{ conversation_handle = DdeConnect( instance_id=0, service=0, topic=0, context=nil) }}
|
235
235
|
spec{ use{ conversation_handle = dde_connect( instance_id=0, service=0, topic=0, context=nil) }}
|
236
|
-
|
236
|
+
|
237
|
+
it 'connects to existing DDE server'
|
237
238
|
end
|
238
239
|
|
239
240
|
describe '#dde_disconnect' do
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'win/error'
|
3
|
+
require 'win/gui/window'
|
4
|
+
|
5
|
+
module WinErrorTest
|
6
|
+
include WinTest
|
7
|
+
include Win::Error
|
8
|
+
include Win::GUI::Window
|
9
|
+
|
10
|
+
def buffer
|
11
|
+
FFI::MemoryPointer.new(:char, 1024)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe Win::Error, ' contains a set of pre-defined Windows API functions' do
|
15
|
+
describe "#get_last_error" do
|
16
|
+
spec{ use{ err_code = GetLastError() }}
|
17
|
+
spec{ use{ err_message = get_last_error() }}
|
18
|
+
|
19
|
+
it "original api retrieves the calling thread's last-error code value" do
|
20
|
+
find_window(TEST_IMPOSSIBLE, TEST_IMPOSSIBLE)
|
21
|
+
GetLastError().should == ERROR_FILE_NOT_FOUND
|
22
|
+
window_text(0)
|
23
|
+
GetLastError().should == ERROR_INVALID_WINDOW_HANDLE
|
24
|
+
end
|
25
|
+
|
26
|
+
it "enhanced api retrieves the message corresponding to last error code" do
|
27
|
+
find_window(TEST_IMPOSSIBLE, TEST_IMPOSSIBLE)
|
28
|
+
get_last_error.should == "The system cannot find the file specified."
|
29
|
+
window_text(0)
|
30
|
+
get_last_error.should == "Invalid window handle."
|
31
|
+
end
|
32
|
+
|
33
|
+
end # describe "#get_last_error"
|
34
|
+
|
35
|
+
describe "#format_message" do
|
36
|
+
spec{ use{ num_chars = FormatMessage(dw_flags=0, lp_source=nil, dw_message_id=0, dw_language_id=0, buffer, buffer.size, :int, 0) }}
|
37
|
+
spec{ use{ message = format_message(dw_flags=0, lp_source=nil, dw_message_id=0, dw_language_id=0, :int, 0) }}
|
38
|
+
|
39
|
+
it "original api Formats a message string. The function requires a message definition as input. The message definition " do
|
40
|
+
pending
|
41
|
+
num_chars = FormatMessage(dw_flags=0, nil, dw_message_id=0, dw_language_id=0, buffer, buffer.size, :int, 0)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "snake_case api Formats a message string. The function requires a message definition as input. The message definition " do
|
45
|
+
pending
|
46
|
+
message = format_message(dw_flags=0, nil, dw_message_id=0, dw_language_id=0, lp_buffer=0, n_size=0,:int, 0)
|
47
|
+
end
|
48
|
+
|
49
|
+
end # describe format_message
|
50
|
+
|
51
|
+
|
52
|
+
end # describe Win::Error
|
53
|
+
end
|
data/spec/win/extensions_spec.rb
CHANGED
@@ -14,16 +14,26 @@ module WinTest
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
context '#camel_case' do
|
18
|
+
it 'transforms snake_case strings' do
|
19
|
+
'get_char_width_32'.camel_case.should == 'GetCharWidth32'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'leaves CamelCase strings intact' do
|
23
|
+
'GetCharWidth32'.camel_case.should == 'GetCharWidth32'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
17
27
|
context '#to_w' do
|
18
28
|
it 'transcodes string to utf-16LE' do
|
19
29
|
'GetCharWidth32'.to_w.encoding.name.should == 'UTF-16LE'
|
20
30
|
end
|
21
|
-
|
31
|
+
|
22
32
|
it 'ensures that encoded string is null-terminated' do
|
23
33
|
'GetCharWidth32'.to_w.bytes.to_a[-2..-1].should == [0, 0]
|
24
34
|
end
|
25
35
|
end
|
26
|
-
|
36
|
+
|
27
37
|
context '#to_vkeys' do
|
28
38
|
it 'transforms number char into [equivalent key code]' do
|
29
39
|
('0'..'9').each {|char| char.to_vkeys.should == char.unpack('C')}
|
data/spec/win/gui/input_spec.rb
CHANGED
@@ -14,15 +14,15 @@ module WinWindowTest
|
|
14
14
|
|
15
15
|
it 'synthesizes a numeric keystrokes, emulating keyboard driver' do
|
16
16
|
test_app do |app|
|
17
|
-
text = '
|
17
|
+
text = '12 34'
|
18
18
|
text.upcase.each_byte do |b| # upcase needed since user32 keybd_event expects upper case chars
|
19
19
|
keybd_event(b.ord, 0, KEYEVENTF_KEYDOWN, 0)
|
20
20
|
sleep TEST_KEY_DELAY
|
21
21
|
keybd_event(b.ord, 0, KEYEVENTF_KEYUP, 0)
|
22
22
|
sleep TEST_KEY_DELAY
|
23
23
|
end
|
24
|
-
app.textarea.
|
25
|
-
|
24
|
+
text(app.textarea).should =~ Regexp.new(text)
|
25
|
+
5.times {keystroke(VK_CONTROL, 'Z'.ord)} # rolling back changes to allow window closing without dialog!
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -46,8 +46,8 @@ module WinWindowTest
|
|
46
46
|
test_app do |app|
|
47
47
|
keystroke(VK_CONTROL, 'A'.ord)
|
48
48
|
keystroke(VK_SPACE)
|
49
|
-
app.textarea.
|
50
|
-
2.times {keystroke(VK_CONTROL, 'Z'.ord)} #
|
49
|
+
text(app.textarea).should.should == ' '
|
50
|
+
2.times {keystroke(VK_CONTROL, 'Z'.ord)} # rolling back changes to allow window closing without dialog!
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -59,8 +59,8 @@ module WinWindowTest
|
|
59
59
|
test_app do |app|
|
60
60
|
text = '12 34'
|
61
61
|
type_in(text)
|
62
|
-
app.textarea.
|
63
|
-
5.times {keystroke(VK_CONTROL, 'Z'.ord)} #
|
62
|
+
text(app.textarea).should =~ Regexp.new(text)
|
63
|
+
5.times {keystroke(VK_CONTROL, 'Z'.ord)} # rolling back changes to allow window closing without dialog!
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|