win 0.0.4 → 0.0.6
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.
- data/README.rdoc +12 -13
- data/Rakefile +3 -2
- data/VERSION +1 -1
- data/lib/win/dde.rb +223 -0
- data/lib/win/library.rb +162 -151
- data/lib/win/window/extra.rb +113 -0
- data/lib/win/window.rb +280 -222
- data/spec/spec_helper.rb +15 -7
- data/spec/win/dde_spec.rb +68 -0
- data/spec/win/window/extra_spec.rb +181 -0
- data/spec/win/window_spec.rb +232 -5
- data/win.gemspec +13 -4
- metadata +20 -4
data/lib/win/window.rb
CHANGED
@@ -5,6 +5,12 @@ module Win
|
|
5
5
|
# Contains constants, functions and wrappers related to Windows manipulation
|
6
6
|
#
|
7
7
|
module Window
|
8
|
+
# Internal constants:
|
9
|
+
|
10
|
+
# Windows keyboard-related Constants:
|
11
|
+
# ? move to keyboard.rb?
|
12
|
+
KEY_DELAY = 0.00001
|
13
|
+
|
8
14
|
include Win::Library
|
9
15
|
|
10
16
|
#General constants:
|
@@ -16,6 +22,70 @@ module Win
|
|
16
22
|
# Sys Command Close
|
17
23
|
SC_CLOSE = 0xF060
|
18
24
|
|
25
|
+
# Key down keyboard event (the key is being depressed)
|
26
|
+
KEYEVENTF_KEYDOWN = 0
|
27
|
+
# Key up keyboard event (the key is being released)
|
28
|
+
KEYEVENTF_KEYUP = 2
|
29
|
+
# Extended kb event. If specified, the scan code was preceded by a prefix byte having the value 0xE0 (224).
|
30
|
+
KEYEVENTF_EXTENDEDKEY = 1
|
31
|
+
|
32
|
+
# Virtual key codes:
|
33
|
+
|
34
|
+
# Control-break processing
|
35
|
+
VK_CANCEL = 0x03
|
36
|
+
# Backspace? key
|
37
|
+
VK_BACK = 0x08
|
38
|
+
# Tab key
|
39
|
+
VK_TAB = 0x09
|
40
|
+
# Shift key
|
41
|
+
VK_SHIFT = 0x10
|
42
|
+
# Ctrl key
|
43
|
+
VK_CONTROL = 0x11
|
44
|
+
# ENTER key
|
45
|
+
VK_RETURN = 0x0D
|
46
|
+
# ALT key
|
47
|
+
VK_ALT = 0x12
|
48
|
+
# ALT key alias
|
49
|
+
VK_MENU = 0x12
|
50
|
+
# PAUSE key
|
51
|
+
VK_PAUSE = 0x13
|
52
|
+
# CAPS LOCK key
|
53
|
+
VK_CAPITAL = 0x14
|
54
|
+
# ESC key
|
55
|
+
VK_ESCAPE = 0x1B
|
56
|
+
# SPACEBAR
|
57
|
+
VK_SPACE = 0x20
|
58
|
+
# PAGE UP key
|
59
|
+
VK_PRIOR = 0x21
|
60
|
+
# PAGE DOWN key
|
61
|
+
VK_NEXT = 0x22
|
62
|
+
# END key
|
63
|
+
VK_END = 0x23
|
64
|
+
# HOME key
|
65
|
+
VK_HOME = 0x24
|
66
|
+
# LEFT ARROW key
|
67
|
+
VK_LEFT = 0x25
|
68
|
+
# UP ARROW key
|
69
|
+
VK_UP = 0x26
|
70
|
+
# RIGHT ARROW key
|
71
|
+
VK_RIGHT = 0x27
|
72
|
+
# DOWN ARROW key
|
73
|
+
VK_DOWN = 0x28
|
74
|
+
# SELECT key
|
75
|
+
VK_SELECT = 0x29
|
76
|
+
# PRINT key
|
77
|
+
VK_PRINT = 0x2A
|
78
|
+
# EXECUTE key
|
79
|
+
VK_EXECUTE = 0x2B
|
80
|
+
# PRINT SCREEN key
|
81
|
+
VK_SNAPSHOT = 0x2C
|
82
|
+
# INS key
|
83
|
+
VK_INSERT = 0x2D
|
84
|
+
# DEL key
|
85
|
+
VK_DELETE = 0x2E
|
86
|
+
# HELP key
|
87
|
+
VK_HELP = 0x2F
|
88
|
+
|
19
89
|
# ShowWindow constants:
|
20
90
|
|
21
91
|
# Hides the window and activates another window.
|
@@ -51,6 +121,50 @@ module Win
|
|
51
121
|
# flag when minimizing windows from a different thread.
|
52
122
|
SW_FORCEMINIMIZE = 11
|
53
123
|
|
124
|
+
class << self
|
125
|
+
# Def_block that calls API function expecting EnumWindowsProc callback (EnumWindows, EnumChildWindows, ...).
|
126
|
+
# Default callback just pushes all passed handles into Array that is returned if Enum function call was successful.
|
127
|
+
# If runtime block is given it is added to the end of default callback (handles Array is still collected/returned).
|
128
|
+
# If Enum function call failed, method returns nil, otherwise an Array of window handles.
|
129
|
+
#
|
130
|
+
def return_enum #:nodoc:
|
131
|
+
lambda do |api, *args, &block|
|
132
|
+
args.push 0 if args.size == api.prototype.size - 2 # If value is missing, it defaults to 0
|
133
|
+
handles = []
|
134
|
+
|
135
|
+
# Insert callback proc into appropriate place of args Array
|
136
|
+
args[api.prototype.find_index(:enum_callback), 0] =
|
137
|
+
proc do |handle, message|
|
138
|
+
handles << handle
|
139
|
+
block ? block[handle, message] : true
|
140
|
+
end
|
141
|
+
handles if api.call *args
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Helper method that creates def_block returning (possibly encoded) string as a result of
|
146
|
+
# api function call or nil if zero characters was returned by api call
|
147
|
+
#
|
148
|
+
def return_string( encode = nil ) #:nodoc:
|
149
|
+
lambda do |api, *args|
|
150
|
+
namespace.enforce_count( args, api.prototype, -2)
|
151
|
+
buffer = FFI::MemoryPointer.new :char, 1024
|
152
|
+
buffer.put_string(0, "\x00" * 1023)
|
153
|
+
args += [buffer, 1024]
|
154
|
+
num_chars = api.call(*args)
|
155
|
+
return nil if num_chars == 0
|
156
|
+
if encode
|
157
|
+
string = buffer.get_bytes(0, num_chars*2)
|
158
|
+
string = string.force_encoding('utf-16LE').encode(encode)
|
159
|
+
else
|
160
|
+
string = buffer.get_bytes(0, num_chars)
|
161
|
+
end
|
162
|
+
string.rstrip
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
private :return_enum, :return_string
|
167
|
+
end
|
54
168
|
|
55
169
|
# Windows GUI API definitions:
|
56
170
|
|
@@ -71,7 +185,7 @@ module Win
|
|
71
185
|
# WS_VISIBLE style, it may be true even if the window is totally obscured by other windows.
|
72
186
|
#
|
73
187
|
# :call-seq:
|
74
|
-
# visible?( win_handle )
|
188
|
+
# [window_]visible?( win_handle )
|
75
189
|
#
|
76
190
|
function 'IsWindowVisible', 'L', 'L', aliases: :visible?
|
77
191
|
|
@@ -148,20 +262,6 @@ module Win
|
|
148
262
|
#
|
149
263
|
function 'FindWindowEx', 'LLPP', 'L', zeronil: true
|
150
264
|
|
151
|
-
# Helper method that creates def_block returning (possibly encoded) string as a result of
|
152
|
-
# api function call or nil if zero characters was returned by api call
|
153
|
-
# TODO: should be private
|
154
|
-
def self.return_string( encode = nil ) #:nodoc:
|
155
|
-
lambda do |api, *args|
|
156
|
-
namespace.enforce_count( args, api.prototype, -2)
|
157
|
-
args += [string = buffer, string.length]
|
158
|
-
num_chars = api.call(*args)
|
159
|
-
return nil if num_chars == 0
|
160
|
-
string = string.force_encoding('utf-16LE').encode(encode) if encode
|
161
|
-
string.rstrip
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
265
|
##
|
166
266
|
# Returns the text of the specified window's title bar (if it has one).
|
167
267
|
# If the specified window is a control, the text of the control is copied. However, GetWindowText
|
@@ -195,7 +295,7 @@ module Win
|
|
195
295
|
# of calling GetWindowText.
|
196
296
|
#
|
197
297
|
#:call-seq:
|
198
|
-
# text =
|
298
|
+
# text = [get_]window_text( win_handle )
|
199
299
|
#
|
200
300
|
function 'GetWindowText', 'LPI', 'L', &return_string
|
201
301
|
|
@@ -204,7 +304,7 @@ module Win
|
|
204
304
|
# API improved to require only win_handle and return rstripped string
|
205
305
|
#
|
206
306
|
#:call-seq:
|
207
|
-
# text =
|
307
|
+
# text = [get_]window_text_w( win_handle )
|
208
308
|
#
|
209
309
|
function 'GetWindowTextW', 'LPI', 'L', &return_string('utf-8')
|
210
310
|
|
@@ -227,7 +327,7 @@ module Win
|
|
227
327
|
# Returns: Name of the class or NIL if function fails. For extended error information, call GetLastError.
|
228
328
|
#
|
229
329
|
#:call-seq:
|
230
|
-
# text =
|
330
|
+
# text = [get_]class_name( win_handle )
|
231
331
|
#
|
232
332
|
function 'GetClassName', 'LPI', 'I', &return_string
|
233
333
|
|
@@ -236,7 +336,7 @@ module Win
|
|
236
336
|
# API improved to require only win_handle and return rstripped string
|
237
337
|
#
|
238
338
|
#:call-seq:
|
239
|
-
# text =
|
339
|
+
# text = [get_]class_name_w( win_handle )
|
240
340
|
#
|
241
341
|
function 'GetClassNameW', 'LPI', 'I', &return_string('utf-8')
|
242
342
|
|
@@ -265,12 +365,6 @@ module Win
|
|
265
365
|
show_window(win_handle, SW_HIDE)
|
266
366
|
end
|
267
367
|
|
268
|
-
return_thread_process = lambda do |api, *args|
|
269
|
-
namespace.enforce_count( args, api.prototype, -1)
|
270
|
-
thread = api.call(args.first, process = [1].pack('L'))
|
271
|
-
nonzero_array(thread, *process.unpack('L'))
|
272
|
-
end
|
273
|
-
|
274
368
|
##
|
275
369
|
# Retrieves the identifier of the thread that created the specified window
|
276
370
|
# and, optionally, the identifier of the process that created the window.
|
@@ -287,16 +381,15 @@ module Win
|
|
287
381
|
# Returns: Pair of identifiers of the thread and process_id that created the window.
|
288
382
|
#
|
289
383
|
#:call-seq:
|
290
|
-
# thread, process_id =
|
384
|
+
# thread, process_id = [get_]window_tread_process_id( win_handle )
|
291
385
|
#
|
292
|
-
function 'GetWindowThreadProcessId',
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
end
|
386
|
+
function 'GetWindowThreadProcessId', [:long, :pointer], :long, &->(api, *args) {
|
387
|
+
namespace.enforce_count( args, api.prototype, -1)
|
388
|
+
process = FFI::MemoryPointer.new(:long)
|
389
|
+
process.write_long(1)
|
390
|
+
thread = api.call(args.first, process)
|
391
|
+
thread == 0 ? [nil, nil] : [thread, process.read_long()] }
|
392
|
+
# weird lambda literal instead of block is needed because RDoc goes crazy if block is attached to meta-definition
|
300
393
|
|
301
394
|
##
|
302
395
|
# Retrieves the dimensions of the specified window bounding rectangle.
|
@@ -318,91 +411,128 @@ module Win
|
|
318
411
|
# are exclusive. In other words, the pixel at (right, bottom) lies immediately outside the rectangle.
|
319
412
|
#
|
320
413
|
#:call-seq:
|
321
|
-
# rect =
|
322
|
-
#
|
323
|
-
function 'GetWindowRect', 'LP', 'I',
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
#
|
332
|
-
#
|
333
|
-
#
|
334
|
-
#
|
335
|
-
#
|
336
|
-
#
|
337
|
-
#
|
338
|
-
#
|
339
|
-
#
|
340
|
-
#
|
341
|
-
#
|
342
|
-
#
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
#
|
347
|
-
#
|
348
|
-
#
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
#
|
378
|
-
#
|
379
|
-
#
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
#
|
403
|
-
|
404
|
-
|
405
|
-
#
|
414
|
+
# rect = [get_]window_rect( win_handle )
|
415
|
+
#
|
416
|
+
function 'GetWindowRect', 'LP', 'I', &->(api, *args) {
|
417
|
+
namespace.enforce_count( args, api.prototype, -1)
|
418
|
+
rect = FFI::MemoryPointer.new(:long, 4)
|
419
|
+
rect.write_array_of_long([0, 0, 0, 0])
|
420
|
+
res = api.call args.first, rect
|
421
|
+
res == 0 ? [nil, nil, nil, nil] : rect.read_array_of_long(4) }
|
422
|
+
# weird lambda literal instead of block is needed because RDoc goes crazy if block is attached to meta-definition
|
423
|
+
|
424
|
+
# This is an application-defined callback function that receives top-level window handles as a result of a call
|
425
|
+
# to the EnumWindows, EnumChildWindows or EnumDesktopWindows function.
|
426
|
+
#
|
427
|
+
# Syntax: BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam );
|
428
|
+
#
|
429
|
+
# Parameters:
|
430
|
+
# hwnd (L) - [out] Handle to a top-level window.
|
431
|
+
# lParam (L) - [in] Specifies the application-defined value given in EnumWindows or EnumDesktopWindows.
|
432
|
+
# Return Values:
|
433
|
+
# TRUE continues enumeration. FALSE stops enumeration.
|
434
|
+
#
|
435
|
+
# Remarks: An application must register this callback function by passing its address to EnumWindows or EnumDesktopWindows.
|
436
|
+
callback :enum_callback, 'LL', :bool
|
437
|
+
|
438
|
+
##
|
439
|
+
# The EnumWindows function enumerates all top-level windows on the screen by passing the handle to
|
440
|
+
# each window, in turn, to an application-defined callback function. EnumWindows continues until
|
441
|
+
# the last top-level window is enumerated or the callback function returns FALSE.
|
442
|
+
#
|
443
|
+
# Original Parameters:
|
444
|
+
# callback [K] - Pointer to an application-defined callback function (see EnumWindowsProc).
|
445
|
+
# value [P] - Specifies an application-defined value(message) to be passed to the callback function.
|
446
|
+
# Original Return: Nonzero if the function succeeds, zero if the function fails. GetLastError for error info.
|
447
|
+
# If callback returns zero, the return value is also zero. In this case, the callback function should
|
448
|
+
# call SetLastError to obtain a meaningful error code to be returned to the caller of EnumWindows.
|
449
|
+
#
|
450
|
+
# API improved to accept blocks (instead of callback objects) and message as a single arg
|
451
|
+
#
|
452
|
+
# New Parameters:
|
453
|
+
# message [P] - Specifies an application-defined value(message) to be passed to the callback function.
|
454
|
+
# block given to method invocation serves as an application-defined callback function (see EnumWindowsProc).
|
455
|
+
# Returns: True if the function succeeds, false if the function fails. GetLastError for error info.
|
456
|
+
# If callback returns zero, the return value is also zero. In this case, the callback function should
|
457
|
+
# call SetLastError to obtain a meaningful error code to be returned to the caller of EnumWindows.
|
458
|
+
#
|
459
|
+
# Remarks: The EnumWindows function does not enumerate child windows, with the exception of a few top-level
|
460
|
+
# windows owned by the system that have the WS_CHILD style. This function is more reliable than calling
|
461
|
+
# the GetWindow function in a loop. An application that calls GetWindow to perform this task risks being
|
462
|
+
# caught in an infinite loop or referencing a handle to a window that has been destroyed.
|
463
|
+
#
|
464
|
+
#:call-seq:
|
465
|
+
# handles = enum_windows( [value] ) {|handle, message| your callback procedure }
|
466
|
+
#
|
467
|
+
function'EnumWindows', [:enum_callback, :long], :bool, &return_enum
|
468
|
+
|
469
|
+
##
|
470
|
+
#EnumDesktopWindows Function
|
471
|
+
#
|
472
|
+
#Enumerates all top-level windows associated with the specified desktop. It passes the handle to each window, in turn, to an application-defined callback function.
|
473
|
+
#
|
474
|
+
#
|
475
|
+
#Syntax
|
476
|
+
#BOOL WINAPI EnumDesktopWindows(
|
477
|
+
# __in_opt HDESK hDesktop,
|
478
|
+
# __in WNDENUMPROC lpfn,
|
479
|
+
# __in LPARAM lParam
|
480
|
+
#);
|
481
|
+
#
|
482
|
+
#Parameters
|
483
|
+
#hDesktop
|
484
|
+
#A handle to the desktop whose top-level windows are to be enumerated. This handle is returned by the CreateDesktop, GetThreadDesktop, OpenDesktop, or OpenInputDesktop function, and must have the DESKTOP_ENUMERATE access right. For more information, see Desktop Security and Access Rights.
|
485
|
+
#
|
486
|
+
#If this parameter is NULL, the current desktop is used.
|
487
|
+
#
|
488
|
+
#lpfn
|
489
|
+
#A pointer to an application-defined EnumWindowsProc callback function.
|
490
|
+
#
|
491
|
+
#lParam
|
492
|
+
#An application-defined value to be passed to the callback function.
|
493
|
+
#
|
494
|
+
#Return Value
|
495
|
+
#If the function fails or is unable to perform the enumeration, the return value is zero.
|
496
|
+
#
|
497
|
+
#To get extended error information, call GetLastError.
|
498
|
+
#
|
499
|
+
#You must ensure that the callback function sets SetLastError if it fails.
|
500
|
+
#
|
501
|
+
#Windows Server 2003 and Windows XP/2000: If there are no windows on the desktop, GetLastError returns ERROR_INVALID_HANDLE.
|
502
|
+
#Remarks
|
503
|
+
#The EnumDesktopWindows function repeatedly invokes the lpfn callback function until the last top-level window is enumerated or the callback function returns FALSE.
|
504
|
+
#
|
505
|
+
#Requirements
|
506
|
+
#Client Requires Windows Vista, Windows XP, or Windows 2000 Professional.
|
507
|
+
function'EnumDesktopWindows', [:ulong, :enum_callback, :long], :bool, &return_enum
|
508
|
+
|
509
|
+
##
|
510
|
+
# Enumerates child windows to a given window.
|
511
|
+
#
|
512
|
+
# Original Parameters:
|
513
|
+
# parent (L) - Handle to the parent window whose child windows are to be enumerated.
|
514
|
+
# callback [K] - Pointer to an application-defined callback function (see EnumWindowsProc).
|
515
|
+
# message [P] - Specifies an application-defined value(message) to be passed to the callback function.
|
516
|
+
# Original Return: Not used (?!)
|
517
|
+
# If callback returns zero, the return value is also zero. In this case, the callback function should
|
518
|
+
# call SetLastError to obtain a meaningful error code to be returned to the caller of EnumWindows.
|
519
|
+
# If it is nil, this function is equivalent to EnumWindows. Windows 95/98/Me: parent cannot be NULL.
|
520
|
+
#
|
521
|
+
# API improved to accept blocks (instead of callback objects) and parent handle (value is optional, default 0)
|
522
|
+
# New Parameters:
|
523
|
+
# parent (L) - Handle to the parent window whose child windows are to be enumerated.
|
524
|
+
# value (P) - Specifies an application-defined value(message) to be passed to the callback function.
|
525
|
+
# block given to method invocation serves as an application-defined callback function (see EnumChildProc).
|
526
|
+
#
|
527
|
+
# Remarks:
|
528
|
+
# If a child window has created child windows of its own, EnumChildWindows enumerates those windows as well.
|
529
|
+
# A child window that is moved or repositioned in the Z order during the enumeration process will be properly enumerated.
|
530
|
+
# The function does not enumerate a child window that is destroyed before being enumerated or that is created during the enumeration process.
|
531
|
+
#
|
532
|
+
#:call-seq:
|
533
|
+
# handles = enum_child_windows( parent_handle, [value = 0] ) {|handle, message| your callback procedure }
|
534
|
+
#
|
535
|
+
function 'EnumChildWindows', [:ulong, :enum_callback, :long], :bool, &return_enum
|
406
536
|
|
407
537
|
##
|
408
538
|
# GetForegroundWindow function returns a handle to the foreground window (the window with which the user
|
@@ -415,10 +545,13 @@ module Win
|
|
415
545
|
# certain circumstances, such as when a window is losing activation.
|
416
546
|
#
|
417
547
|
#:call-seq:
|
418
|
-
# win_handle =
|
548
|
+
# win_handle = [get_]foreground_window()
|
419
549
|
#
|
420
550
|
function 'GetForegroundWindow', [], 'L'
|
421
551
|
|
552
|
+
##
|
553
|
+
# Tests if given window handle points to foreground (topmost) window
|
554
|
+
#
|
422
555
|
def foreground?(win_handle)
|
423
556
|
win_handle == foreground_window
|
424
557
|
end
|
@@ -436,115 +569,40 @@ module Win
|
|
436
569
|
# To get the window handle to the active window in the message queue for another thread, use GetGUIThreadInfo.
|
437
570
|
#
|
438
571
|
#:call-seq:
|
439
|
-
# win_handle =
|
572
|
+
# win_handle = [get_]active_window()
|
440
573
|
#
|
441
574
|
function 'GetActiveWindow', [], 'L'
|
442
575
|
|
576
|
+
##
|
577
|
+
# The keybd_event function synthesizes a keystroke. The system can use such a synthesized keystroke to generate
|
578
|
+
# a WM_KEYUP or WM_KEYDOWN message. The keyboard driver's interrupt handler calls the keybd_event function.
|
579
|
+
#
|
580
|
+
# !!!! Windows NT/2000/XP/Vista:This function has been superseded. Use SendInput instead.
|
581
|
+
#
|
582
|
+
# Syntax: VOID keybd_event( BYTE bVk, BYTE bScan, DWORD dwFlags, PTR dwExtraInfo);
|
583
|
+
#
|
584
|
+
# Parameters:
|
585
|
+
# bVk [C] - [in] Specifies a virtual-key code. The code must be a value in the range 1 to 254.
|
586
|
+
# For a complete list, see Virtual-Key Codes.
|
587
|
+
# bScan [C] - [in] Specifies a hardware scan code for the key.
|
588
|
+
# dwFlags [L] - [in] Specifies various aspects of function operation. This parameter can be
|
589
|
+
# one or more of the following values:
|
590
|
+
# KEYEVENTF_EXTENDEDKEY, KEYEVENTF_KEYUP, KEYEVENTF_KEYDOWN
|
591
|
+
# dwExtraInfo [L] -[in] Specifies an additional value associated with the key stroke.
|
592
|
+
#
|
593
|
+
# Return Value: none
|
594
|
+
#
|
595
|
+
# Remarks: An application can simulate a press of the PRINTSCRN key in order to obtain a screen snapshot and save
|
596
|
+
# it to the clipboard. To do this, call keybd_event with the bVk parameter set to VK_SNAPSHOT.
|
597
|
+
#
|
598
|
+
# Windows NT/2000/XP: The keybd_event function can toggle the NUM LOCK, CAPS LOCK, and SCROLL LOCK keys.
|
599
|
+
# Windows 95/98/Me: The keybd_event function can toggle only the CAPS LOCK and SCROLL LOCK keys.
|
600
|
+
#
|
443
601
|
function 'keybd_event', 'IILL', 'V'
|
602
|
+
|
444
603
|
function 'PostMessage', 'LLLL', 'L'
|
445
604
|
function 'SendMessage', 'LLLP', 'L'
|
446
605
|
function 'GetDlgItem', 'LL', 'L'
|
447
606
|
|
448
|
-
|
449
|
-
# Convenience wrapper methods:
|
450
|
-
|
451
|
-
# emulates combinations of keys pressed (Ctrl+Alt+P+M, etc)
|
452
|
-
def keystroke(*keys)
|
453
|
-
return if keys.empty?
|
454
|
-
keybd_event keys.first, 0, KEYEVENTF_KEYDOWN, 0
|
455
|
-
sleep WG_KEY_DELAY
|
456
|
-
keystroke *keys[1..-1]
|
457
|
-
sleep WG_KEY_DELAY
|
458
|
-
keybd_event keys.first, 0, KEYEVENTF_KEYUP, 0
|
459
|
-
end
|
460
|
-
|
461
|
-
# types text message into window holding the focus
|
462
|
-
def type_in(message)
|
463
|
-
message.scan(/./m) do |char|
|
464
|
-
keystroke(*char.to_vkeys)
|
465
|
-
end
|
466
|
-
end
|
467
|
-
|
468
|
-
# finds top-level dialog window by title and yields it to given block
|
469
|
-
def dialog(title, seconds=3)
|
470
|
-
d = begin
|
471
|
-
win = Window.top_level(title, seconds)
|
472
|
-
yield(win) ? win : nil
|
473
|
-
rescue TimeoutError
|
474
|
-
end
|
475
|
-
d.wait_for_close if d
|
476
|
-
return d
|
477
|
-
end
|
478
|
-
|
479
|
-
# Thin wrapper around window handle
|
480
|
-
class Window
|
481
|
-
include Win::Window
|
482
|
-
extend Win::Window
|
483
|
-
|
484
|
-
attr_reader :handle
|
485
|
-
|
486
|
-
# find top level window by title, return wrapped Window object
|
487
|
-
def self.top_level(title, seconds=3)
|
488
|
-
@handle = timeout(seconds) do
|
489
|
-
sleep WG_SLEEP_DELAY while (h = find_window nil, title) == nil; h
|
490
|
-
end
|
491
|
-
Window.new @handle
|
492
|
-
end
|
493
|
-
|
494
|
-
def initialize(handle)
|
495
|
-
@handle = handle
|
496
|
-
end
|
497
|
-
|
498
|
-
# find child window (control) by title, window class, or control ID:
|
499
|
-
def child(id)
|
500
|
-
result = case id
|
501
|
-
when String
|
502
|
-
by_title = find_window_ex @handle, 0, nil, id.gsub('_' , '&' )
|
503
|
-
by_class = find_window_ex @handle, 0, id, nil
|
504
|
-
by_title ? by_title : by_class
|
505
|
-
when Fixnum
|
506
|
-
get_dlg_item @handle, id
|
507
|
-
when nil
|
508
|
-
find_window_ex @handle, 0, nil, nil
|
509
|
-
else
|
510
|
-
nil
|
511
|
-
end
|
512
|
-
raise "Control '#{id}' not found" unless result
|
513
|
-
Window.new result
|
514
|
-
end
|
515
|
-
|
516
|
-
def children
|
517
|
-
enum_child_windows(@handle,'Msg').map{|child_handle| Window.new child_handle}
|
518
|
-
end
|
519
|
-
|
520
|
-
# emulate click of the control identified by id
|
521
|
-
def click(id)
|
522
|
-
h = child(id).handle
|
523
|
-
rectangle = [0, 0, 0, 0].pack 'LLLL'
|
524
|
-
get_window_rect h, rectangle
|
525
|
-
left, top, right, bottom = rectangle.unpack 'LLLL'
|
526
|
-
center = [(left + right) / 2, (top + bottom) / 2]
|
527
|
-
set_cursor_pos *center
|
528
|
-
mouse_event MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0
|
529
|
-
mouse_event MOUSEEVENTF_LEFTUP, 0, 0, 0, 0
|
530
|
-
end
|
531
|
-
|
532
|
-
def close
|
533
|
-
post_message @handle, WM_SYSCOMMAND, SC_CLOSE, 0
|
534
|
-
end
|
535
|
-
|
536
|
-
def wait_for_close
|
537
|
-
timeout(WG_CLOSE_TIMEOUT) do
|
538
|
-
sleep WG_SLEEP_DELAY while window_visible?(@handle)
|
539
|
-
end
|
540
|
-
end
|
541
|
-
|
542
|
-
def text
|
543
|
-
buffer = "\x0" * 2048
|
544
|
-
length = send_message @handle, WM_GETTEXT, buffer.length, buffer
|
545
|
-
length == 0 ? '' : buffer[0..length - 1]
|
546
|
-
end
|
547
|
-
end
|
548
|
-
|
549
607
|
end
|
550
608
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,20 +3,27 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
3
3
|
require 'spec'
|
4
4
|
require 'spec/autorun'
|
5
5
|
|
6
|
+
$debug = true
|
7
|
+
|
6
8
|
# Customize RSpec with my own extensions
|
7
9
|
module SpecMacros
|
8
10
|
|
9
11
|
# wrapper for it method that extracts description from example source code, such as:
|
10
12
|
# spec { use{ function(arg1 = 4, arg2 = 'string') }}
|
11
13
|
def spec &block
|
12
|
-
|
14
|
+
# p (block.methods-Object.methods).sort; exit
|
15
|
+
if RUBY_PLATFORM =~ /java/
|
16
|
+
it 'dummy JRuby description', &block
|
17
|
+
else
|
18
|
+
it description_from(*block.source_location), &block
|
19
|
+
end
|
13
20
|
end
|
14
21
|
|
15
22
|
# reads description line from source file and drops external brackets (like its{}, use{}
|
16
23
|
def description_from(file, line)
|
17
|
-
|
18
|
-
|
19
|
-
|
24
|
+
File.open(file) do |f|
|
25
|
+
f.lines.to_a[line-1].gsub( /(spec.*?{)|(use.*?{)|}/, '' ).strip
|
26
|
+
end
|
20
27
|
end
|
21
28
|
end
|
22
29
|
|
@@ -24,6 +31,7 @@ Spec::Runner.configure { |config| config.extend(SpecMacros) }
|
|
24
31
|
|
25
32
|
module WinTest
|
26
33
|
|
34
|
+
TEST_KEY_DELAY = 0.001
|
27
35
|
TEST_IMPOSSIBLE = 'Impossible'
|
28
36
|
TEST_CONVERSION_ERROR = /Can.t convert/
|
29
37
|
TEST_SLEEP_DELAY = 0.01
|
@@ -32,14 +40,15 @@ module WinTest
|
|
32
40
|
TEST_WIN_TITLE = 'LockNote - Steganos LockNote'
|
33
41
|
TEST_WIN_CLASS = 'ATL:00434098'
|
34
42
|
TEST_WIN_RECT = [710, 400, 1210, 800]
|
43
|
+
TEST_STATUSBAR_CLASS = 'msctls_statusbar32'
|
35
44
|
TEST_TEXTAREA_CLASS = 'ATL:00434310'
|
36
45
|
|
37
46
|
def use
|
38
|
-
lambda
|
47
|
+
lambda{yield}.should_not raise_error
|
39
48
|
end
|
40
49
|
|
41
50
|
def any_block
|
42
|
-
lambda
|
51
|
+
lambda{|*args| args}
|
43
52
|
end
|
44
53
|
|
45
54
|
def any_handle
|
@@ -53,5 +62,4 @@ module WinTest
|
|
53
62
|
def not_a_handle
|
54
63
|
123
|
55
64
|
end
|
56
|
-
|
57
65
|
end
|