win 0.3.16 → 0.3.17

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