win 0.3.16 → 0.3.17

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/HISTORY CHANGED
@@ -58,3 +58,7 @@
58
58
  == 0.3.16 / 2010-06-11
59
59
 
60
60
  * System::Version module completed
61
+
62
+ == 0.3.17 / 2010-06-13
63
+
64
+ * Message fucntions now accept both Pointer and Long lParams
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.16
1
+ 0.3.17
data/lib/win/gui/input.rb CHANGED
@@ -193,7 +193,7 @@ module Win
193
193
  # A positive value indicates that the wheel was rotated forward, away from the user; a negative value
194
194
  # indicates that the wheel was rotated backward, toward the user. One wheel click is defined as
195
195
  # WHEEL_DELTA, which is 120.
196
- # - If dwFlags contains MOUSEEVENTF_WHHEEL, then data specifies the amount of
196
+ # - If dwFlags contains MOUSEEVENTF_WHEEL, then data specifies the amount of
197
197
  # wheel movement. A positive value indicates that the wheel was rotated to the right; a negative value
198
198
  # indicates that the wheel was rotated to the left. One wheel click is defined as WHEEL_DELTA (= 120).
199
199
  # - Windows 2000/XP: If flags contains MOUSEEVENTF_XDOWN or MOUSEEVENTF_XUP, then data specifies which
@@ -421,6 +421,7 @@ module Win
421
421
  # {|handle, msg, data, l_result| callback code }
422
422
  #
423
423
  function :SendMessageCallback, [:HWND, :uint, :uint, :pointer, :SendAsyncProc, :ulong], :int8, boolean: true,
424
+ alternative: [[:HWND, :uint, :uint, :long, :SendAsyncProc, :ulong], :int8, ->(*args){Integer === args[3]}],
424
425
  &->(api, handle, msg, w_param, l_param, data=0, &block){
425
426
  api.call(handle, msg, w_param, l_param, block, data)}
426
427
 
@@ -462,28 +463,8 @@ module Win
462
463
  #:call-seq:
463
464
  # success = post_message(handle, msg, w_param, l_param)
464
465
  #
465
- function :PostMessage, [:ulong, :uint, :uint, :pointer], :int,
466
- boolean: true, camel_name: :PostMessagePointer, snake_name: :post_message_pointer
467
- function :PostMessage, [:ulong, :uint, :uint, :long], :int,
468
- boolean: true, camel_name: :PostMessageLong, snake_name: :post_message_long
469
-
470
- def PostMessage(handle, msg, w_param, l_param)
471
- # Routes call depending on lParam type (:pointer or :long)
472
- case l_param
473
- when Fixnum
474
- PostMessageLong(handle, msg, w_param, l_param)
475
- else
476
- PostMessagePointer(handle, msg, w_param, l_param)
477
- end
478
- end
479
-
480
- def post_message(handle, msg, w_param, l_param, &block)
481
- if block
482
- block[PostMessage(handle, msg, w_param, l_param)]
483
- else
484
- PostMessage(handle, msg, w_param, l_param) != 0
485
- end
486
- end
466
+ function :PostMessage, [:ulong, :uint, :uint, :pointer], :int, boolean: true,
467
+ alternative: [[:ulong, :uint, :uint, :long], :int, ->(*args){Integer === args.last}]
487
468
 
488
469
  ##
489
470
  # The SendMessage function sends the specified message to a window or windows. It calls the window procedure for
@@ -526,7 +507,8 @@ module Win
526
507
  #:call-seq:
527
508
  # send_message(handle, msg, w_param, l_param)
528
509
  #
529
- function :SendMessage, [:ulong, :uint, :uint, :pointer], :int # LPARAM different from PostMessage!
510
+ function :SendMessage, [:ulong, :uint, :uint, :pointer], :int,
511
+ alternative: [[:ulong, :uint, :uint, :long], :int, ->(*args){Integer === args.last}]
530
512
 
531
513
  ##
532
514
  # :method: :SendMessageLong?
data/lib/win/library.rb CHANGED
@@ -311,16 +311,20 @@ module Win
311
311
  ffi_lib *(ffi_libraries.map(&:name) << options[:dll]) if options[:dll]
312
312
  libs = ffi_libraries.map(&:name)
313
313
 
314
- effective_name = if options[:alternative]
314
+ alternative = options.delete(:alternative) # Function may have alternative signature
315
+ effective_name = if alternative
315
316
 
316
- alt_params, alt_returns, condition = generate_signature(*options.dup[:alternative])
317
+ alt_params, alt_returns, condition = generate_signature(*alternative)
317
318
  api = function name, params, returns,
318
- options.dup.merge( camel_only: true, camel_name: "#{camel_name}Original")
319
+ options.merge( camel_only: true, camel_name: "#{camel_name}Original")
319
320
  alt_api = function name, alt_params, alt_returns,
320
- options.dup.merge( camel_only: true, camel_name: "#{camel_name}Alternative")
321
+ options.merge( camel_only: true, camel_name: "#{camel_name}Alternative")
322
+
321
323
  define_method camel_name do |*args|
322
324
  (condition[*args] ? alt_api : api).call(*args)
323
325
  end
326
+ module_function camel_name
327
+ public camel_name
324
328
  api.effective_name
325
329
  else
326
330
  effective_names.inject(nil) do |func, effective_name|
@@ -349,21 +353,10 @@ module Win
349
353
  # Generate body for snake_case method
350
354
  method_body = generate_snake_method_body(api, options, &def_block)
351
355
 
352
- # Define snake_case instance method
356
+ # Define snake_case as both instance and module-level method
353
357
  define_method snake_name, &method_body
354
-
355
- # We need to define module(class) level method, but something went wrong with module_function :(
356
- # module_function snake_name # TODO: Doesn't work as a perfect replacement for eigen_class stuff. :( Why?
357
-
358
- # OK, instead of module_method we're going to directly modify eigenclass
359
- eigen_class = class << self;
360
- self;
361
- end
362
-
363
- # Define snake_case class method inside eigenclass, that should do it
364
- eigen_class.class_eval do
365
- define_method snake_name, &method_body
366
- end
358
+ module_function snake_name
359
+ public snake_name
367
360
 
368
361
  # Define (instance method!) aliases, if any
369
362
  aliases.each {|ali| alias_method ali, snake_name }
@@ -466,7 +459,8 @@ module Win
466
459
  # Win::Library::API is a wrapper for callable function API object that mimics Win32::API
467
460
  class API
468
461
 
469
- # The name of the DLL(s) that export this API function
462
+ # The name of the DLL(s) that export this API function.
463
+ # dll_name alias needed for compatibility with Win32::API interface
470
464
  attr_reader :dll
471
465
  alias_method :dll_name, :dll
472
466
 
@@ -478,7 +472,9 @@ module Win
478
472
 
479
473
  # The name of the actual Windows API function. For example, if you passed 'GetUserName' to the
480
474
  # constructor, then the effective function name would be either 'GetUserNameA' or 'GetUserNameW'.
481
- attr_accessor :effective_function_name
475
+ # effective_function_name alias needed for compatibility with Win32::API interface
476
+ attr_accessor :effective_name
477
+ alias_method :effective_function_name, :effective_name
482
478
 
483
479
  # The prototype, returned as an array of FFI types
484
480
  attr_reader :prototype
@@ -486,10 +482,10 @@ module Win
486
482
  # The return type (:void for no return value)
487
483
  attr_reader :return_type
488
484
 
489
- def initialize( namespace, function_name, effective_function_name, prototype, return_type, dll )
485
+ def initialize( namespace, function_name, effective_name, prototype, return_type, dll )
490
486
  @namespace = namespace
491
487
  @function_name = function_name.to_sym
492
- @effective_function_name = effective_function_name.to_sym
488
+ @effective_name = effective_name.to_sym
493
489
  @prototype = prototype
494
490
  @return_type = return_type
495
491
  @dll = dll
@@ -3,13 +3,13 @@ require 'win/library'
3
3
  module Win
4
4
  module System
5
5
 
6
- # Contains constants and Win32 API functions related to dialog manipulation.
7
- # Windows dialog basics can be found here:
8
- # http://msdn.microsoft.com/en-us/library/ms644996#init_box
6
+ # Contains constants and Win32 API functions related to system information functions.
7
+ # More functions of this type can be found here:
8
+ # http://msdn.microsoft.com/en-us/library/ms724953%28v=VS.85%29.aspx
9
9
  module Info
10
10
  extend Win::Library
11
11
 
12
- # Enum COMPUTER_NAME_FORMAT (for GetComputerNameEx)
12
+ # Enum COMPUTER_NAME_FORMAT (for GetComputerNameEx). TODO: convert into FFI::Enum
13
13
 
14
14
  ComputerNameNetBIOS = 0
15
15
  ComputerNameDnsHostname = 1
@@ -39,7 +39,20 @@ module WinGuiInputTest
39
39
 
40
40
  describe '#mouse_event' do
41
41
  spec { use {mouse_event( flags = MOUSEEVENTF_ABSOLUTE, dx = 0, dy = 0, data=0, extra_info=0 )}}
42
- it 'Emulates Mouse clicks'
42
+
43
+ it 'emulates mouse clicks' do
44
+ test_app do |app|
45
+ # Position cursor at app's "Close Window" control
46
+ left, top, right, bottom = get_window_rect(app.handle)
47
+ set_cursor_pos(x=right-5, y=top+5).should be_true
48
+
49
+ mouse_event MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0
50
+ mouse_event MOUSEEVENTF_LEFTUP, 0, 0, 0, 0
51
+ sleep SLEEP_DELAY
52
+ window?(app.handle).should == false
53
+ end
54
+ end
55
+
43
56
  end # describe '#mouse_event'
44
57
 
45
58
  describe "#get_cursor_pos" do
@@ -15,7 +15,7 @@ module WinGuiMessageTest
15
15
  end
16
16
 
17
17
  def msg
18
- @msg ||=Win::Gui::Message::Msg.new
18
+ @msg ||= Win::Gui::Message::Msg.new
19
19
  end
20
20
 
21
21
  def msg_callback
@@ -83,21 +83,32 @@ module WinGuiMessageTest
83
83
  end # describe '#post_message'
84
84
 
85
85
  describe '#send_message' do
86
+ after(:all){close_test_app if @launched_test_app}
87
+
86
88
  spec{ use{ success = SendMessage(handle = 0, msg = 0, w_param = 0, l_param = nil) }}
87
89
  spec{ use{ success = send_message(handle = 0, msg = 0, w_param = 0, l_param = nil) }}
88
90
 
89
- it 'directly sends the specified message to a window or windows' do
90
- app = launch_test_app
91
-
92
- num_chars = send_message app.handle, WM_GETTEXT, buffer.size, buffer
93
- buffer.get_bytes(0, num_chars).should == "LockNote - Steganos LockNote"
91
+ context 'sends a message to the specified window' do
92
+ it 'with nil as last argument(lParam)' do
93
+ app = launch_test_app
94
+ send_message(app.handle, WM_SYSCOMMAND, SC_CLOSE, nil)
95
+ sleep SLEEP_DELAY
96
+ window?(app.handle).should == false
97
+ end
94
98
 
95
- num_chars = send_message app.textarea, WM_GETTEXT, buffer.size, buffer
96
- buffer.get_bytes(0, num_chars).should =~ /Welcome to Steganos LockNote/
99
+ it 'with pointer as last argument(lParam)' do
100
+ app = launch_test_app
101
+ send_message(app.handle, WM_SYSCOMMAND, SC_CLOSE, FFI::MemoryPointer.new(:long))
102
+ sleep SLEEP_DELAY
103
+ window?(app.handle).should == false
104
+ end
97
105
 
98
- send_message(app.handle, WM_SYSCOMMAND, SC_CLOSE, nil)
99
- sleep SLEEP_DELAY # delay to allow window close
100
- window?(app.handle).should == false
106
+ it 'with Fixnum as last argument(lParam)' do
107
+ app = launch_test_app
108
+ send_message(app.handle, WM_SYSCOMMAND, SC_CLOSE, 0)
109
+ sleep SLEEP_DELAY
110
+ window?(app.handle).should == false
111
+ end
101
112
  end
102
113
  end # describe '#send_message'
103
114
 
@@ -111,13 +122,13 @@ module WinGuiMessageTest
111
122
  it "sends message to window and returns, specifying callback to be called by system after message is processed" do
112
123
  sent = SendMessageCallback(@app.handle, WM_USER, 0, nil, msg_callback, data=13)
113
124
  sent.should == 1
114
- @handle.should == nil
125
+ @handle.should == nil # Callback did not fire just yet
115
126
  @message.should == nil
116
127
  @data.should == nil
117
128
  @result.should == nil
118
129
 
119
- sleep SLEEP_DELAY # small delay to allow message delivery
120
- peek_message # dispatching sent message (even though there is nothing in queue)
130
+ sleep SLEEP_DELAY # Small delay to allow message delivery
131
+ peek_message # Dispatching sent message (even though there is nothing in queue)
121
132
 
122
133
  @handle.should == @app.handle
123
134
  @message.should == WM_USER
@@ -126,16 +137,26 @@ module WinGuiMessageTest
126
137
  end
127
138
 
128
139
  it "snake_case api defaults data to 0, converts block into callback and returns true/false" do
129
- sent = send_message_callback(@app.handle, WM_USER, 0, nil){|*args|@data=args[2]}
140
+ sent = send_message_callback(@app.handle, WM_USER, 0, nil){|*args| @data=args[2]}
130
141
  sent.should == true
131
- @data.should == nil
142
+ @data.should == nil # Callback did not fire just yet
132
143
 
133
- sleep SLEEP_DELAY # small delay to allow message delivery
134
- peek_message # dispatching sent message (even though there is nothing in queue)
144
+ sleep SLEEP_DELAY # Small delay to allow message delivery
145
+ peek_message # Dispatching sent message (even though there is nothing in queue)
135
146
 
136
147
  @data.should == 0
137
148
  end
138
149
 
150
+ it "can be used with :pointer as a 4th arg" do
151
+ sent = send_message_callback(@app.handle, WM_USER, 0, FFI::MemoryPointer.new(:long)){|*args|}
152
+ sent.should == true
153
+ end
154
+
155
+ it "can be used with :long as a 4th arg" do
156
+ sent = send_message_callback(@app.handle, WM_USER, 0, 0){|*args|}
157
+ sent.should == true
158
+ end
159
+
139
160
  it "fails if unable to send message" do
140
161
  sent = SendMessageCallback(not_a_handle, WM_USER, 0, nil, msg_callback, 0)
141
162
  sent.should == 0
@@ -468,25 +468,50 @@ module WinGuiWindowTest
468
468
  end
469
469
  end # context GA_PARENT
470
470
 
471
- # GA_ROOT - Retrieves the root window by walking the chain of parent windows.
471
+ context 'GA_ROOT - Retrieves the root window by walking the chain of parent windows' do
472
+ it "retrieves a handle to the specified window's root (top level parent)" do
473
+ child = find_window_ex(@app.handle, 0, nil, nil)
474
+ root1 = GetAncestor(child, GA_ROOT)
475
+ root2 = get_ancestor(child, GA_ROOT)
476
+ root1.should == root2
477
+ root1.should == @app.handle
478
+ end
479
+ end
472
480
  # GA_ROOTOWNER - Retrieves the owned root window by walking the chain of parent and owner windows
473
481
  # returned by GetParent.
474
- it "original api retrieves the handle to the ancestor of the specified window. " do
475
- pending
476
- success = GetAncestor(hwnd=0, ga_flags=0)
477
- end
478
-
479
- it "snake_case api retrieves the handle to the ancestor of the specified window. " do
480
- pending
481
- success = get_ancestor(hwnd=0, ga_flags=0)
482
- end
483
-
484
482
  end # describe get_ancestor
485
483
 
486
484
  describe "#get_window" do
487
485
  spec{ use{ handle = GetWindow(any_handle, command=0) }}
488
486
  spec{ use{ handle = get_window(any_handle, command=0) }}
489
487
 
488
+ # GW_ENABLEDPOPUP - Windows 2000/XP: The retrieved handle identifies the enabled popup window owned by
489
+
490
+ context 'GW_HWND... works with windows of the same type' do
491
+ before(:all) do
492
+ @statusbar = find_window_ex(@app.handle, 0, STATUSBAR_CLASS, nil)
493
+ @textarea = find_window_ex(@app.handle, 0, TEXTAREA_CLASS, nil)
494
+ end
495
+
496
+ it 'GW_HWNDFIRST - The retrieved handle identifies the window of the same type that is highest in Z order' do
497
+ get_window(@statusbar, GW_HWNDFIRST).should == @statusbar
498
+ end
499
+ it 'GW_HWNDLAST - The retrieved handle identifies the window of the same type that is lowest in the Z order' do
500
+ get_window(@statusbar, GW_HWNDLAST).should == @textarea
501
+ end
502
+ it 'GW_HWNDNEXT - The retrieved handle identifies the window below the specified window in the Z order' do
503
+ get_window(@statusbar, GW_HWNDNEXT).should == @textarea
504
+ end
505
+ it 'GW_HWNDPREV - The retrieved handle identifies the window above the specified window in the Z order' do
506
+ get_window(@textarea, GW_HWNDPREV).should == @statusbar
507
+ end
508
+
509
+ it 'returns nil/0 in case nothing is returned' do
510
+ get_window(@textarea, GW_HWNDNEXT).should == nil
511
+ GetWindow(@textarea, GW_HWNDNEXT).should == 0
512
+ end
513
+ end # context 'GW_HWND...
514
+
490
515
  context "GW_CHILD retrieves a window handle to a first (top of the Z order) child of given window" do
491
516
  before(:all) do
492
517
  # GW_CHILD - The retrieved handle identifies the child window at the top of the Z order, if the specified
@@ -510,11 +535,12 @@ module WinGuiWindowTest
510
535
  end # context GW_CHILD
511
536
 
512
537
  context "GW_OWNER - retrieves a handle to an owner of a given Window" do
513
- # GW_OWNER - The retrieved handle identifies the specified window's owner window, if any. For more
538
+ # Ownership is a relationship between two top level windows while Parent is a relationship between a top
539
+ # level and a WS_CHILD, or a WS_CHILD and another WS_CHILD. The parent of a button is the form it is on,
540
+ # while a message box is owned by the form that showed it.
514
541
 
515
542
  it 'returns owner (but NOT parent!) of a given window' do
516
- pending
517
-
543
+ pending 'Need to open modal dialog, app should be it`s owner'
518
544
  child = find_window_ex(@app.handle, 0, nil, nil)
519
545
  p owner1 = GetWindow(child, GW_OWNER)
520
546
  p owner2 = get_window(child, GW_OWNER)
@@ -526,27 +552,12 @@ module WinGuiWindowTest
526
552
  GetWindow(@app.handle, GW_OWNER).should == 0
527
553
  get_window(@app.handle, GW_OWNER).should == nil
528
554
  end
529
-
530
- end
531
-
532
- it "GW_OWNER - retrieves a handle to a window that has the specified relationship to given Window" do
533
- pending
534
- # GW_ENABLEDPOPUP - Windows 2000/XP: The retrieved handle identifies the enabled popup window owned by
535
- # GW_HWNDFIRST - The retrieved handle identifies the window of the same type that is highest in Z order.
536
- # GW_HWNDLAST - The retrieved handle identifies the window of the same type that is lowest in the Z order.
537
- # GW_HWNDNEXT - The retrieved handle identifies the window below the specified window in the Z order.
538
- # GW_HWNDPREV - The retrieved handle identifies the window above the specified window in the Z order.
539
- # GW_OWNER - The retrieved handle identifies the specified window's owner window, if any. For more
540
555
  end
541
556
  end # describe get_window
542
557
 
543
558
  describe '#enum_windows' do
544
- # before(:each){@app = launch_test_app}
545
- # after(:each){close_test_app}
546
559
 
547
560
  spec{ use{ handles = enum_windows(value = 13) }}
548
- spec{ use{ enum_windows do |handle, message|
549
- end }}
550
561
 
551
562
  it 'iterates through all the top-level windows, passing each top level window handle and value to a given block' do
552
563
  enum_windows(13) do |handle, message|
@@ -558,7 +569,6 @@ module WinGuiWindowTest
558
569
 
559
570
  it 'returns an array of top-level window handles if block is not given' do
560
571
  enum = enum_windows(13)
561
- # p enum
562
572
  enum.should be_a_kind_of Array
563
573
  enum.should_not be_empty
564
574
  enum.should have_at_least(5).elements # typical number of top windows in WinXP system?
@@ -1,5 +1,6 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
  require 'win/library'
3
+ require 'win/gui/message'
3
4
 
4
5
  module WinLibraryTest
5
6
 
@@ -43,7 +44,7 @@ module WinLibraryTest
43
44
  end
44
45
 
45
46
  def redefined_methods
46
- [:FindWindow, :IsWindow, :EnumWindows, :GetUserName, :GetForegroundWindow, :keybd_event]
47
+ [:FindWindow, :IsWindow, :EnumWindows, :GetUserName, :GetForegroundWindow, :keybd_event, :SendMessage]
47
48
  end
48
49
 
49
50
  def hide_method(*names) # hide original method(s) if it is defined
@@ -296,6 +297,30 @@ module WinLibraryTest
296
297
  end
297
298
  end
298
299
 
300
+ context 'defining API function with alternative signature' do
301
+ before(:each) do
302
+ @def_block = nil
303
+ MyLib.function :SendMessage, [:ulong, :uint, :uint, :pointer], :int,
304
+ alternative: [[:ulong, :uint, :uint, :long], :int, ->(*args){Integer === args.last}]
305
+ end
306
+
307
+ it 'defines camel and snake methods (as well as hidden Original/Alternative methods)' do
308
+ expect { send_message(any_handle, Win::Gui::Message::WM_GETTEXT, buffer.size, buffer) }.to_not raise_error
309
+ expect { send_message(any_handle, Win::Gui::Message::WM_GETTEXT, buffer.size, buffer.address) }.to_not raise_error
310
+ end
311
+
312
+ it 'defines camel and snake methods that work with both signatures' do
313
+ respond_to?(:SendMessage).should be_true
314
+ respond_to?(:send_message).should be_true
315
+ respond_to?(:SendMessageOriginal).should be_true
316
+ respond_to?(:SendMessageAlternative).should be_true
317
+ MyLib.respond_to?(:SendMessage).should be_true
318
+ MyLib.respond_to?(:send_message).should be_true
319
+ MyLib.respond_to?(:SendMessageOriginal).should be_true
320
+ MyLib.respond_to?(:SendMessageAlternative).should be_true
321
+ end
322
+ end
323
+
299
324
  context 'trying to define an invalid API function' do
300
325
  it 'raises error when trying to define function with a wrong function name' do
301
326
  expect { MyLib.function 'FindWindowImpossible', 'PP', 'L' }.
@@ -330,7 +355,7 @@ module WinLibraryTest
330
355
  end
331
356
  end
332
357
 
333
- context 'defining API function that has snake_case name' do
358
+ context 'defining API function that has original snake_case name' do
334
359
  it 'should define original function in (generated) CamelCase' do
335
360
  MyLib.function :keybd_event, [:char, :char, :ulong, :ulong], :void
336
361
  expect {KeybdEvent(Win::Gui::Input::VK_CONTROL, 0, Win::Gui::Input::KEYEVENTF_KEYDOWN, 0)}.to_not raise_error
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 3
8
- - 16
9
- version: 0.3.16
8
+ - 17
9
+ version: 0.3.17
10
10
  platform: ruby
11
11
  authors:
12
12
  - arvicco
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-06-11 00:00:00 +04:00
17
+ date: 2010-06-13 00:00:00 +04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency