win 0.1.13 → 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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, [:ulong], :int, boolean: true
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, [:ulong], :int, boolean: true, aliases: :visible?
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, [:ulong], :int, boolean: true, aliases: :maximized?
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, [:ulong], :int, boolean: true, aliases: :minimized?
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, [:ulong, :ulong], :int, boolean: true
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], :ulong, zeronil: true
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], :ulong, zeronil: true
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, [:ulong, :ulong, :pointer, :pointer], :ulong, zeronil: true
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, [:ulong, :pointer, :int], :int, &return_string
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, [:ulong, :pointer, :int], :int, &return_string('utf-8')
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, [:ulong, :pointer, :int], :int, &return_string
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, [:ulong, :pointer, :int], :int, &return_string('utf-8')
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, [:ulong, :int], :int, boolean: true,
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, [:ulong, :pointer], :long,
396
- &->(api, *args) {
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(args.first, process)
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, [:ulong, :pointer], :int,
429
- &->(api, *args) {
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 args.first, rect
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, [:ulong, :long], :bool
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, [:ulong, :EnumWindowsProc, :long], :bool, &return_enum
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, [], :ulong, zeronil: true
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, [], :ulong, zeronil: true
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 typedefs:
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: :long, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
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
- method_name, effective_names, aliases = generate_names(name, options)
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
- method_body = if def_block
343
- if zeronil
344
- ->(*args, &block){ (res = def_block.(api, *args, &block)) != 0 ? res : nil }
345
- elsif boolean
346
- ->(*args, &block){ def_block.(api, *args, &block) != 0 }
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
- ->(*args, &block){ def_block.(api, *args, &block) }
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
- else
351
- if zeronil
352
- ->(*args, &block){ (res = block ? block[api[*args]] : api[*args]) != 0 ? res : nil }
353
- elsif boolean
354
- ->(*args, &block){ block ? block[api[*args]] : api[*args] != 0 }
355
- else
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
- define_method method_name, &method_body # define snake_case method
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
- method_name = options[:rename] || name.snake_case
376
- case method_name
386
+ snake_name = options[:rename] || name.snake_case
387
+ case snake_name
377
388
  when /^is_/
378
- aliases << method_name.sub(/^is_/, '') + '?'
389
+ aliases << snake_name.sub(/^is_/, '') + '?'
379
390
  when /^set_/
380
- aliases << method_name.sub(/^set_/, '')+ '='
391
+ aliases << snake_name.sub(/^set_/, '')+ '='
381
392
  when /^get_/
382
- aliases << method_name.sub(/^get_/, '')
393
+ aliases << snake_name.sub(/^get_/, '')
383
394
  end
384
- [method_name, effective_names, aliases]
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
- @launched_test_app = Window::Window.new handle
90
- # app = "Test app" #need to get rid of Window for JRuby
91
- # class << app; self; end.send( :define_method, :handle, &lambda {handle})
92
- # @launched_test_app = app
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 and app.respond_to? :handle and find_window(nil, TEST_WIN_TITLE)
97
- post_message(app.handle, WM_SYSCOMMAND, SC_CLOSE, 0)
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
- it 'connects to existing DDE server'
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
@@ -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')}
@@ -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 = '123 456'
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.text.should =~ Regexp.new(text)
25
- 7.times {keystroke(VK_CONTROL, 'Z'.ord)} # dirty hack!
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.text.should == ' '
50
- 2.times {keystroke(VK_CONTROL, 'Z'.ord)} # dirty hack!
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.text.should =~ Regexp.new(text)
63
- 5.times {keystroke(VK_CONTROL, 'Z'.ord)} # dirty hack!
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