rdp-rautomation 0.6.3.1

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.
Files changed (120) hide show
  1. data/.document +5 -0
  2. data/.rspec +2 -0
  3. data/.yardopts +6 -0
  4. data/History.rdoc +103 -0
  5. data/LICENSE +20 -0
  6. data/README.rdoc +114 -0
  7. data/Rakefile +43 -0
  8. data/VERSION +1 -0
  9. data/ext/AutoItX/AutoItX.chm +0 -0
  10. data/ext/AutoItX/AutoItX3.dll +0 -0
  11. data/ext/IAccessibleDLL/IAccessibleDLL.sln +20 -0
  12. data/ext/IAccessibleDLL/IAccessibleDLL.suo +0 -0
  13. data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.cpp +30 -0
  14. data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.vcxproj +102 -0
  15. data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.vcxproj.filters +42 -0
  16. data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.vcxproj.user +3 -0
  17. data/ext/IAccessibleDLL/IAccessibleDLL/ReadMe.txt +48 -0
  18. data/ext/IAccessibleDLL/IAccessibleDLL/dllmain.cpp +19 -0
  19. data/ext/IAccessibleDLL/IAccessibleDLL/stdafx.cpp +8 -0
  20. data/ext/IAccessibleDLL/IAccessibleDLL/stdafx.h +22 -0
  21. data/ext/IAccessibleDLL/IAccessibleDLL/table_support.cpp +282 -0
  22. data/ext/IAccessibleDLL/IAccessibleDLL/targetver.h +8 -0
  23. data/ext/IAccessibleDLL/Release/IAccessibleDLL.dll +0 -0
  24. data/ext/ListViewExplorer/ListViewExplorer.sln +20 -0
  25. data/ext/ListViewExplorer/ListViewExplorer.suo +0 -0
  26. data/ext/ListViewExplorer/ListViewExplorer/ListViewExplorer.cpp +174 -0
  27. data/ext/ListViewExplorer/ListViewExplorer/ListViewExplorer.vcxproj +95 -0
  28. data/ext/ListViewExplorer/ListViewExplorer/ListViewExplorer.vcxproj.filters +42 -0
  29. data/ext/ListViewExplorer/ListViewExplorer/ListViewExplorer.vcxproj.user +3 -0
  30. data/ext/ListViewExplorer/ListViewExplorer/ReadMe.txt +40 -0
  31. data/ext/ListViewExplorer/ListViewExplorer/stdafx.cpp +8 -0
  32. data/ext/ListViewExplorer/ListViewExplorer/stdafx.h +17 -0
  33. data/ext/ListViewExplorer/ListViewExplorer/table_support.cpp +250 -0
  34. data/ext/ListViewExplorer/ListViewExplorer/table_support.h +2 -0
  35. data/ext/ListViewExplorer/ListViewExplorer/targetver.h +8 -0
  36. data/ext/UiaDll/Release/UiaDll.dll +0 -0
  37. data/ext/UiaDll/UiaDll.sln +20 -0
  38. data/ext/UiaDll/UiaDll.suo +0 -0
  39. data/ext/UiaDll/UiaDll/ReadMe.txt +48 -0
  40. data/ext/UiaDll/UiaDll/UiaDll.cpp +205 -0
  41. data/ext/UiaDll/UiaDll/UiaDll.vcxproj +104 -0
  42. data/ext/UiaDll/UiaDll/UiaDll.vcxproj.filters +42 -0
  43. data/ext/UiaDll/UiaDll/dllmain.cpp +39 -0
  44. data/ext/UiaDll/UiaDll/globals.h +3 -0
  45. data/ext/UiaDll/UiaDll/stdafx.cpp +8 -0
  46. data/ext/UiaDll/UiaDll/stdafx.h +19 -0
  47. data/ext/UiaDll/UiaDll/targetver.h +8 -0
  48. data/ext/WindowsForms/bin/WindowsForms.exe +0 -0
  49. data/ext/WindowsForms/src/WindowsForms/WindowsForms.sln +20 -0
  50. data/ext/WindowsForms/src/WindowsForms/WindowsForms.suo +0 -0
  51. data/ext/WindowsForms/src/WindowsForms/WindowsForms/AboutBox.Designer.cs +80 -0
  52. data/ext/WindowsForms/src/WindowsForms/WindowsForms/AboutBox.cs +103 -0
  53. data/ext/WindowsForms/src/WindowsForms/WindowsForms/AboutBox.resx +120 -0
  54. data/ext/WindowsForms/src/WindowsForms/WindowsForms/DataEntryForm.Designer.cs +187 -0
  55. data/ext/WindowsForms/src/WindowsForms/WindowsForms/DataEntryForm.cs +46 -0
  56. data/ext/WindowsForms/src/WindowsForms/WindowsForms/DataEntryForm.resx +120 -0
  57. data/ext/WindowsForms/src/WindowsForms/WindowsForms/MainFormWindow.Designer.cs +377 -0
  58. data/ext/WindowsForms/src/WindowsForms/WindowsForms/MainFormWindow.cs +78 -0
  59. data/ext/WindowsForms/src/WindowsForms/WindowsForms/MainFormWindow.resx +120 -0
  60. data/ext/WindowsForms/src/WindowsForms/WindowsForms/PersonForm.Designer.cs +119 -0
  61. data/ext/WindowsForms/src/WindowsForms/WindowsForms/PersonForm.cs +34 -0
  62. data/ext/WindowsForms/src/WindowsForms/WindowsForms/PersonForm.resx +120 -0
  63. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Program.cs +21 -0
  64. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/AssemblyInfo.cs +36 -0
  65. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Resources.Designer.cs +71 -0
  66. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Resources.resx +117 -0
  67. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Settings.Designer.cs +30 -0
  68. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Settings.settings +7 -0
  69. data/ext/WindowsForms/src/WindowsForms/WindowsForms/SimpleElementsForm.Designer.cs +93 -0
  70. data/ext/WindowsForms/src/WindowsForms/WindowsForms/SimpleElementsForm.cs +19 -0
  71. data/ext/WindowsForms/src/WindowsForms/WindowsForms/SimpleElementsForm.resx +120 -0
  72. data/ext/WindowsForms/src/WindowsForms/WindowsForms/WindowsForms.csproj +123 -0
  73. data/ext/WindowsForms/src/WindowsForms/WindowsForms/bin/Release/WindowsForms.exe +0 -0
  74. data/lib/rautomation.rb +6 -0
  75. data/lib/rautomation/adapter/autoit.rb +5 -0
  76. data/lib/rautomation/adapter/autoit/button.rb +59 -0
  77. data/lib/rautomation/adapter/autoit/locators.rb +22 -0
  78. data/lib/rautomation/adapter/autoit/text_field.rb +61 -0
  79. data/lib/rautomation/adapter/autoit/window.rb +184 -0
  80. data/lib/rautomation/adapter/helper.rb +20 -0
  81. data/lib/rautomation/adapter/win_ffi.rb +21 -0
  82. data/lib/rautomation/adapter/win_ffi/button.rb +25 -0
  83. data/lib/rautomation/adapter/win_ffi/button_helper.rb +24 -0
  84. data/lib/rautomation/adapter/win_ffi/checkbox.rb +19 -0
  85. data/lib/rautomation/adapter/win_ffi/constants.rb +94 -0
  86. data/lib/rautomation/adapter/win_ffi/control.rb +79 -0
  87. data/lib/rautomation/adapter/win_ffi/functions.rb +333 -0
  88. data/lib/rautomation/adapter/win_ffi/keystroke_converter.rb +67 -0
  89. data/lib/rautomation/adapter/win_ffi/label.rb +21 -0
  90. data/lib/rautomation/adapter/win_ffi/list_box.rb +60 -0
  91. data/lib/rautomation/adapter/win_ffi/locators.rb +22 -0
  92. data/lib/rautomation/adapter/win_ffi/ms_uia/uia_dll.rb +36 -0
  93. data/lib/rautomation/adapter/win_ffi/radio.rb +19 -0
  94. data/lib/rautomation/adapter/win_ffi/select_list.rb +87 -0
  95. data/lib/rautomation/adapter/win_ffi/table.rb +57 -0
  96. data/lib/rautomation/adapter/win_ffi/text_field.rb +52 -0
  97. data/lib/rautomation/adapter/win_ffi/window.rb +226 -0
  98. data/lib/rautomation/button.rb +55 -0
  99. data/lib/rautomation/element_collections.rb +47 -0
  100. data/lib/rautomation/text_field.rb +60 -0
  101. data/lib/rautomation/wait_helper.rb +23 -0
  102. data/lib/rautomation/window.rb +234 -0
  103. data/spec/adapter/win_ffi/button_spec.rb +41 -0
  104. data/spec/adapter/win_ffi/checkbox_spec.rb +48 -0
  105. data/spec/adapter/win_ffi/keystroke_converter_spec.rb +47 -0
  106. data/spec/adapter/win_ffi/label_spec.rb +21 -0
  107. data/spec/adapter/win_ffi/listbox_spec.rb +52 -0
  108. data/spec/adapter/win_ffi/radio_spec.rb +37 -0
  109. data/spec/adapter/win_ffi/select_list_spec.rb +66 -0
  110. data/spec/adapter/win_ffi/table_spec.rb +39 -0
  111. data/spec/adapter/win_ffi/text_field_spec.rb +23 -0
  112. data/spec/adapter/win_ffi/window_spec.rb +43 -0
  113. data/spec/button_spec.rb +68 -0
  114. data/spec/buttons_spec.rb +21 -0
  115. data/spec/spec_helper.rb +96 -0
  116. data/spec/text_field_spec.rb +65 -0
  117. data/spec/text_fields_spec.rb +22 -0
  118. data/spec/window_spec.rb +122 -0
  119. data/spec/windows_spec.rb +55 -0
  120. metadata +207 -0
@@ -0,0 +1,20 @@
1
+ module RAutomation
2
+ module Adapter
3
+ autoload :Autoit, File.dirname(__FILE__) + "/autoit.rb"
4
+ autoload :WinFfi, File.dirname(__FILE__) + "/win_ffi.rb"
5
+
6
+ module Helper
7
+ extend self
8
+
9
+ # @private
10
+ # Retrieves default {Adapter} for the current platform.
11
+ def default_adapter
12
+ if ENV['OS'] == 'Windows_NT'
13
+ :win_ffi
14
+ else
15
+ raise "unsupported platform for RAutomation: #{RUBY_PLATFORM}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ begin
2
+ gem "ffi"
3
+ rescue Gem::LoadError
4
+ raise Gem::LoadError, "Unable to load FFI gem. Install it with:\n\tgem install ffi"
5
+ end
6
+ require "ffi"
7
+ require File.dirname(__FILE__) + "/win_ffi/constants"
8
+ require File.dirname(__FILE__) + "/win_ffi/keystroke_converter"
9
+ require File.dirname(__FILE__) + "/win_ffi/functions"
10
+ require File.dirname(__FILE__) + "/win_ffi/locators"
11
+ require File.dirname(__FILE__) + "/win_ffi/window"
12
+ require File.dirname(__FILE__) + "/win_ffi/button_helper"
13
+ require File.dirname(__FILE__) + "/win_ffi/control"
14
+ require File.dirname(__FILE__) + "/win_ffi/button"
15
+ require File.dirname(__FILE__) + "/win_ffi/checkbox"
16
+ require File.dirname(__FILE__) + "/win_ffi/radio"
17
+ require File.dirname(__FILE__) + "/win_ffi/text_field"
18
+ require File.dirname(__FILE__) + "/win_ffi/select_list"
19
+ require File.dirname(__FILE__) + "/win_ffi/table"
20
+ require File.dirname(__FILE__) + "/win_ffi/label"
21
+ require File.dirname(__FILE__) + "/win_ffi/list_box"
@@ -0,0 +1,25 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module WinFfi
4
+ class Button < Control
5
+ include WaitHelper
6
+ include Locators
7
+
8
+ # Default locators used for searching buttons.
9
+ DEFAULT_LOCATORS = {:class => /button/i}
10
+
11
+ # @see RAutomation::Button#value
12
+ def value
13
+ Functions.control_value(Functions.control_hwnd(@window.hwnd, @locators))
14
+ end
15
+
16
+ def exist?
17
+ @locators[:id].nil? ? super : super && matches_type(Constants::UIA_BUTTON_CONTROL_TYPE)
18
+ end
19
+
20
+ alias_method :exists?, :exist?
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module WinFfi
4
+ module ButtonHelper
5
+
6
+ def set?
7
+ control_hwnd = Functions.control_hwnd(@window.hwnd, @locators)
8
+ Functions.control_set? control_hwnd
9
+ end
10
+
11
+ # @todo call a windows function to do this without clicking
12
+ def clear
13
+ click {!set?} if set?
14
+ end
15
+
16
+ # @todo call a windows function to do this without clicking
17
+ def set
18
+ click {set?} unless set?
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module WinFfi
4
+ class Checkbox < Control
5
+ include WaitHelper
6
+ include Locators
7
+ include ButtonHelper
8
+
9
+
10
+ def exist?
11
+ @locators[:id].nil? ? super : super && matches_type(Constants::UIA_CHECKBOX_CONTROL_TYPE)
12
+ end
13
+
14
+ alias_method :exists?, :exist?
15
+
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,94 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module WinFfi
4
+ # @private
5
+ module Constants
6
+ WM_GETTEXT = 0xD
7
+ WM_SETTEXT = 0xC
8
+ WM_GETTEXTLENGTH = 0xE
9
+ WM_CLOSE = 0x10
10
+
11
+ SW_MAXIMIZE = 3
12
+ SW_MINIMIZE = 6
13
+ SW_RESTORE = 9
14
+
15
+ SMTO_ABORTIFHUNG = 0x2
16
+
17
+ STANDARD_RIGHTS_REQUIRED = 0xF0000
18
+ SYNCHRONIZE = 0x100000
19
+ PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF
20
+
21
+ BM_CLICK = 0xF5
22
+ BM_GETSTATE = 0xF2
23
+ BST_CHECKED = 0x1
24
+
25
+ # keybd_event constants
26
+ KEYEVENTF_EXTENDEDKEY = 0x1
27
+ KEYEVENTF_KEYUP = 0x2
28
+
29
+ VK_BACK = 0x08
30
+ VK_TAB = 0x09
31
+ VK_RETURN = 0x0D
32
+ VK_SPACE = 0x20
33
+ VK_CAPITAL = 0x14
34
+ VK_LEFT = 0x25
35
+ VK_UP = 0x26
36
+ VK_RIGHT = 0x27
37
+ VK_DOWN = 0x28
38
+ VK_SHIFT = 0x10
39
+ VK_LSHIFT = 0xA0
40
+ VK_RSHIFT = 0xA1
41
+ VK_MENU = 0x12
42
+ VK_LMENU = 0xA4
43
+ VK_RMENU = 0xA5
44
+ VK_CONTROL = 0x11
45
+ VK_LCONTROL = 0xA2
46
+ VK_RCONTROL = 0xA3
47
+ VK_ESCAPE = 0x1B
48
+ VK_END = 0x23
49
+ VK_HOME = 0x24
50
+ VK_NUMLOCK = 0x90
51
+ VK_DELETE = 0x2E
52
+ VK_INSERT = 0x2D
53
+
54
+ # GetWindow constants
55
+ GW_ENABLEDPOPUP = 6
56
+
57
+ # HRESULT
58
+ S_OK = 0
59
+
60
+ # IAccessible Button States
61
+ STATE_SYSTEM_UNAVAILABLE = 0x00000001
62
+ STATE_SYSTEM_SELECTED = 0x00000002
63
+ STATE_SYSTEM_FOCUSED = 0x00000004
64
+ STATE_SYSTEM_CHECKED = 0x00000010
65
+
66
+ # Combobox
67
+ CB_GETCOUNT = 0x0146
68
+ CB_GETLBTEXTLEN = 0x0149
69
+ CB_GETLBTEXT = 0x0148
70
+ CB_GETCURSEL = 0x0147
71
+ CB_ERR = -1
72
+ CB_SETCURSEL = 0x14E
73
+
74
+ # listview
75
+ LVM_FIRST = 0x1000
76
+ LVM_GETITEMCOUNT = LVM_FIRST + 4
77
+
78
+ # UI Automation control type IDs
79
+ UIA_LIST_CONTROL_TYPE = 50008
80
+ UIA_LIST_ITEM_CONTROL_TYPE = 50007
81
+ UIA_CHECKBOX_CONTROL_TYPE = 50002
82
+ UIA_BUTTON_CONTROL_TYPE = 50000
83
+ UIA_LABEL_CONTROL_TYPE = 50020
84
+ UIA_RADIO_BUTTON_CONTROL_TYPE = 50013
85
+ UIA_COMBOBOX_CONTROL_TYPE = 50003
86
+ UIA_EDIT_CONTROL_TYPE = 50004
87
+ UIA_HEADER_CONTROL_TYPE = 50034
88
+ UIA_HEADER_ITEM_CONTROL_TYPE = 50035
89
+
90
+
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,79 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module WinFfi
4
+ class Control
5
+ include WaitHelper
6
+ include Locators
7
+
8
+ # Creates the control object.
9
+ # @note this method is not meant to be accessed directly
10
+ # @param [RAutomation::Window] window this button belongs to.
11
+ # @param [Hash] locators for searching the button.
12
+ # @option locators [String, Regexp] :value Value (text) of the button
13
+ # @option locators [String, Regexp] :class Internal class name of the button
14
+ # @option locators [String, Fixnum] :id Internal ID of the button
15
+ # @option locators [String, Fixnum] :index 0-based index to specify n-th button if all other criteria match
16
+ # @see RAutomation::Window#button
17
+ def initialize(window, locators)
18
+ @window = window
19
+ extract(locators)
20
+ end
21
+
22
+ def click
23
+ assert_enabled
24
+ clicked = false
25
+ wait_until do
26
+ hwnd = Functions.control_hwnd(@window.hwnd, @locators)
27
+
28
+ @window.activate
29
+ @window.active? &&
30
+ Functions.set_control_focus(hwnd) &&
31
+ Functions.control_click(hwnd) &&
32
+ clicked = true # is clicked at least once
33
+
34
+ block_given? ? yield : clicked && !exist?
35
+ end
36
+ end
37
+
38
+ def exist?
39
+ !!Functions.control_hwnd(@window.hwnd, @locators)
40
+ end
41
+
42
+ def enabled?
43
+ !disabled?
44
+ end
45
+
46
+ def disabled?
47
+ Functions.unavailable?(Functions.control_hwnd(@window.hwnd, @locators))
48
+ end
49
+
50
+ def has_focus?
51
+ Functions.has_focus?(Functions.control_hwnd(@window.hwnd, @locators))
52
+ end
53
+
54
+ def set_focus
55
+ assert_enabled
56
+ uia_control = UiaDll::element_from_handle(Functions.control_hwnd(@window.hwnd, @locators))
57
+ UiaDll::set_focus(uia_control)
58
+ end
59
+
60
+ def uia_control(automation_id)
61
+ uia_window = UiaDll::element_from_handle(@window.hwnd) # finds IUIAutomationElement for given parent window
62
+ uia_element = UiaDll::find_child_by_id(uia_window, automation_id.to_s)
63
+ fail "Cannot find UIAutomationElement" if uia_element.nil?
64
+ uia_element
65
+ end
66
+
67
+ def matches_type(clazz)
68
+ UiaDll::current_control_type(uia_control(@locators[:id])) == clazz
69
+ end
70
+
71
+ alias_method :exists?, :exist?
72
+
73
+ def assert_enabled
74
+ raise "Cannot interact with disabled control #{@locators.inspect} on window #{@window.locators.inspect}!" if disabled?
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,333 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module WinFfi
4
+ # @private
5
+ module Functions
6
+ extend FFI::Library
7
+
8
+ # TODO once done adapt the path to the DLL (somewhere in the packaged gem)
9
+ ffi_lib 'user32', 'kernel32', 'ole32', File.dirname(__FILE__) + '/../../../../ext/IAccessibleDLL/Release/iaccessibleDll.dll'
10
+ ffi_convention :stdcall
11
+
12
+ callback :enum_callback, [:long, :pointer], :bool
13
+
14
+ # user32
15
+ attach_function :enum_windows, :EnumWindows,
16
+ [:enum_callback, :pointer], :long
17
+ attach_function :enum_child_windows, :EnumChildWindows,
18
+ [:long, :enum_callback, :pointer], :long
19
+ attach_function :_close_window, :CloseWindow,
20
+ [:long], :bool
21
+ attach_function :minimized, :IsIconic,
22
+ [:long], :bool
23
+ attach_function :_window_title, :GetWindowTextA,
24
+ [:long, :pointer, :int], :int
25
+ attach_function :window_title_length, :GetWindowTextLengthA,
26
+ [:long], :int
27
+ attach_function :window_exists, :IsWindow,
28
+ [:long], :bool
29
+ attach_function :_window_class, :GetClassNameA,
30
+ [:long, :pointer, :int], :int
31
+ attach_function :window_visible, :IsWindowVisible,
32
+ [:long], :bool
33
+ attach_function :show_window, :ShowWindow,
34
+ [:long, :int], :bool
35
+ attach_function :send_message, :SendMessageA,
36
+ [:long, :uint, :uint, :pointer], :long
37
+ attach_function :send_message_timeout, :SendMessageTimeoutA,
38
+ [:long, :uint, :uint, :pointer, :uint, :uint, :pointer], :bool
39
+ attach_function :post_message, :PostMessageA,
40
+ [:long, :uint, :uint, :pointer], :bool
41
+ attach_function :window_thread_process_id, :GetWindowThreadProcessId,
42
+ [:long, :pointer], :long
43
+ attach_function :attach_thread_input, :AttachThreadInput,
44
+ [:long, :long, :bool], :bool
45
+ attach_function :set_foreground_window, :SetForegroundWindow,
46
+ [:long], :bool
47
+ attach_function :bring_window_to_top, :BringWindowToTop,
48
+ [:long], :bool
49
+ attach_function :set_active_window, :SetActiveWindow,
50
+ [:long], :long
51
+ attach_function :foreground_window, :GetForegroundWindow,
52
+ [], :long
53
+ attach_function :send_key, :keybd_event,
54
+ [:uchar, :uchar, :int, :pointer], :void
55
+ attach_function :control_id, :GetDlgCtrlID,
56
+ [:long], :int
57
+ attach_function :_set_control_focus, :SetFocus,
58
+ [:long], :long
59
+ attach_function :get_window, :GetWindow,
60
+ [:long, :uint], :long
61
+ attach_function :get_last_error, :GetLastError,
62
+ [], :long
63
+
64
+ # kernel32
65
+ attach_function :open_process, :OpenProcess,
66
+ [:int, :bool, :int], :long
67
+ attach_function :terminate_process, :TerminateProcess,
68
+ [:long, :uint], :bool
69
+ attach_function :close_handle, :CloseHandle,
70
+ [:long], :bool
71
+ attach_function :load_library, :LoadLibraryA,
72
+ [:string], :long
73
+
74
+ # ole32
75
+ attach_function :co_initialize, :CoInitialize,
76
+ [:pointer], :uint16
77
+
78
+ # iaccessible
79
+ attach_function :get_button_state, :get_button_state,
80
+ [:long], :long
81
+ attach_function :get_table_row_strings, :get_table_row_strings,
82
+ [:long, :long, :pointer, :long, :pointer], :void
83
+ attach_function :select_table_row, :select_table_row,
84
+ [:long, :long, :long], :void
85
+ attach_function :get_table_row_state, :get_table_row_state,
86
+ [:long, :long, :long], :long
87
+
88
+ class << self
89
+
90
+ def window_title(hwnd)
91
+ title_length = window_title_length(hwnd) + 1
92
+ title = FFI::MemoryPointer.new :char, title_length
93
+ _window_title(hwnd, title, title_length)
94
+ title.read_string
95
+ end
96
+
97
+ alias_method :control_title, :window_title
98
+
99
+ def window_text(hwnd)
100
+ found_text = ""
101
+ window_callback = FFI::Function.new(:bool, [:long, :pointer], {:convention => :stdcall}) do |child_hwnd, _|
102
+ found_text << text_for(child_hwnd)
103
+ true
104
+ end
105
+ enum_child_windows(hwnd, window_callback, nil)
106
+ found_text
107
+ end
108
+
109
+ alias_method :control_text, :window_text
110
+
111
+ def window_hwnd(locators)
112
+ find_hwnd(locators) do |hwnd|
113
+ window_visible(hwnd) && locators_match?(locators, window_properties(hwnd, locators))
114
+ end
115
+ end
116
+
117
+ def child_window_locators(parent_hwnd, locators)
118
+ child_hwnd = locators[:hwnd] || child_hwnd(parent_hwnd, locators)
119
+ if child_hwnd
120
+ locators.merge!(:hwnd => child_hwnd)
121
+ else
122
+ popup_hwnd = get_window(parent_hwnd, Constants::GW_ENABLEDPOPUP)
123
+ if popup_hwnd != parent_hwnd
124
+ popup_properties = window_properties(popup_hwnd, locators)
125
+ locators.merge!(:hwnd => popup_hwnd) if locators_match?(locators, popup_properties)
126
+ end
127
+ end
128
+ locators
129
+ end
130
+
131
+ def child_window_locators(parent_hwnd, locators)
132
+ child_hwnd = locators[:hwnd] || child_hwnd(parent_hwnd, locators)
133
+ if child_hwnd
134
+ locators.merge!(:hwnd => child_hwnd)
135
+ else
136
+ popup_hwnd = get_window(parent_hwnd, Constants::GW_ENABLEDPOPUP)
137
+ if popup_hwnd != parent_hwnd
138
+ popup_properties = window_properties(popup_hwnd, locators)
139
+ locators.merge!(:hwnd => popup_hwnd) if locators_match?(locators, popup_properties)
140
+ end
141
+ end
142
+ locators
143
+ end
144
+
145
+ def window_pid(hwnd)
146
+ pid = FFI::MemoryPointer.new :int
147
+ window_thread_process_id(hwnd, pid)
148
+ pid.read_int
149
+ end
150
+
151
+ def window_class(hwnd)
152
+ class_name = FFI::MemoryPointer.new :char, 512
153
+ _window_class(hwnd, class_name, 512)
154
+ class_name.read_string
155
+ end
156
+
157
+ alias_method :control_class, :window_class
158
+
159
+ def close_window(hwnd)
160
+ _close_window(hwnd)
161
+ closed = send_message_timeout(hwnd, Constants::WM_CLOSE,
162
+ 0, nil, Constants::SMTO_ABORTIFHUNG, 1000, nil)
163
+ # force it to close
164
+ unless closed
165
+ process_hwnd = open_process(Constants::PROCESS_ALL_ACCESS, false, window_pid(hwnd))
166
+ terminate_process(process_hwnd, 0)
167
+ close_handle(process_hwnd)
168
+ end
169
+ end
170
+
171
+ def activate_window(hwnd)
172
+ set_foreground_window(hwnd)
173
+ set_active_window(hwnd)
174
+ bring_window_to_top(hwnd)
175
+ within_foreground_thread(hwnd) do
176
+ set_foreground_window(hwnd)
177
+ set_active_window(hwnd)
178
+ bring_window_to_top(hwnd)
179
+ end
180
+ end
181
+
182
+ def control_hwnd(window_hwnd, locators)
183
+ if locators[:id].nil?
184
+ find_hwnd(locators, window_hwnd) do |hwnd|
185
+ locators_match?(locators, control_properties(hwnd, locators))
186
+ end
187
+ else
188
+ uia_window = UiaDll::element_from_handle(window_hwnd) # finds IUIAutomationElement for given parent window
189
+ uia_control = UiaDll::find_child_by_id(uia_window, locators[:id].to_s)
190
+ hwnd = UiaDll::current_native_window_handle(uia_control) # return HWND of UIA element
191
+ raise UnknownElementException, "#{locators[:id]} does not exist" if hwnd == 0
192
+ hwnd
193
+ end
194
+ end
195
+
196
+ alias_method :child_hwnd, :control_hwnd
197
+
198
+ def control_value(control_hwnd)
199
+ text_for(control_hwnd)
200
+ end
201
+
202
+ def control_click(control_hwnd)
203
+ post_message(control_hwnd, Constants::BM_CLICK, 0, nil)
204
+ end
205
+
206
+ def set_control_focus(control_hwnd)
207
+ within_foreground_thread control_hwnd do
208
+ _set_control_focus(control_hwnd)
209
+ end
210
+ end
211
+
212
+ def set_control_text(control_hwnd, text)
213
+ send_message(control_hwnd, Constants::WM_SETTEXT, 0, text)
214
+ end
215
+
216
+ def control_set?(control_hwnd)
217
+ get_button_state(control_hwnd) & Constants::STATE_SYSTEM_CHECKED != 0
218
+ end
219
+
220
+ def has_focus?(control_hwnd)
221
+ get_button_state(control_hwnd) & Constants::STATE_SYSTEM_FOCUSED != 0
222
+ end
223
+
224
+ def unavailable?(control_hwnd)
225
+ get_button_state(control_hwnd) & Constants::STATE_SYSTEM_UNAVAILABLE != 0
226
+ end
227
+
228
+ def retrieve_combobox_item_text(control_hwnd, item_no)
229
+ text_len = send_message(control_hwnd, Constants::CB_GETLBTEXTLEN, item_no, nil)
230
+
231
+ string_buffer = FFI::MemoryPointer.new :char, text_len
232
+ send_message(control_hwnd, Constants::CB_GETLBTEXT, item_no, string_buffer)
233
+ string_buffer.read_string
234
+ end
235
+
236
+ def control_name(control_hwnd)
237
+ string_buffer = FFI::MemoryPointer.new :char, 255
238
+ if (get_control_name(control_hwnd, string_buffer) == Constants::S_OK)
239
+ string_buffer.read_string
240
+ else
241
+ fail "Cannot get name for control with HWND 0x" + control_hwnd.to_s(16)
242
+ end
243
+ end
244
+
245
+ def retrieve_table_strings_for_row(control_hwnd, row)
246
+ hModule = load_library("oleacc.dll") # TODO should be done only one time
247
+
248
+ strings_ptr = FFI::MemoryPointer.new :pointer
249
+ columns_ptr = FFI::MemoryPointer.new :pointer
250
+
251
+ get_table_row_strings(hModule, control_hwnd, strings_ptr, row, columns_ptr)
252
+ str_ptr = strings_ptr.read_pointer
253
+ columns = columns_ptr.read_long
254
+
255
+ str_ptr.get_array_of_string(0, columns)
256
+ end
257
+
258
+ private
259
+
260
+ def within_foreground_thread(hwnd)
261
+ foreground_thread = window_thread_process_id(foreground_window, nil)
262
+ other_thread = window_thread_process_id(hwnd, nil)
263
+ attach_thread_input(foreground_thread, other_thread, true) unless other_thread == foreground_thread
264
+ yield
265
+ ensure
266
+ attach_thread_input(foreground_thread, other_thread, false) unless other_thread == foreground_thread
267
+ end
268
+
269
+ def window_properties(hwnd, locators)
270
+ element_properties(:window, hwnd, locators)
271
+ end
272
+
273
+ def control_properties(hwnd, locators)
274
+ element_properties(:control, hwnd, locators)
275
+ end
276
+
277
+ def element_properties(type, hwnd, locators)
278
+ locators.inject({}) do |properties, locator|
279
+ properties[locator[0]] = self.send("#{type}_#{locator[0]}", hwnd) unless locator[0] == :index
280
+ properties
281
+ end
282
+ end
283
+
284
+ def locators_match?(locators, properties)
285
+ locators.all? do |locator, value|
286
+ if locator == :index
287
+ true
288
+ elsif value.is_a?(Regexp)
289
+ properties[locator] =~ value
290
+ else
291
+ properties[locator] == value
292
+ end
293
+ end
294
+ end
295
+
296
+ def find_hwnd(locators, window_hwnd = nil)
297
+ found_hwnd = nil
298
+ found_index = -1
299
+ window_callback = FFI::Function.new(:bool, [:long, :pointer], {:convention => :stdcall}) do |hwnd, _|
300
+ if yield(hwnd)
301
+ found_index += 1
302
+
303
+ if locators[:index]
304
+ found_hwnd = hwnd if locators[:index] == found_index
305
+ else
306
+ found_hwnd = hwnd
307
+ end
308
+ end
309
+ !found_hwnd
310
+ end
311
+
312
+ unless window_hwnd
313
+ enum_windows(window_callback, nil)
314
+ else
315
+ enum_child_windows(window_hwnd, window_callback, nil)
316
+ end
317
+
318
+ found_hwnd
319
+ end
320
+
321
+ def text_for(hwnd)
322
+ text_length = send_message(hwnd, Constants::WM_GETTEXTLENGTH, 0, nil) + 1
323
+ text = FFI::MemoryPointer.new :char, text_length
324
+ send_message(hwnd, Constants::WM_GETTEXT, text_length, text)
325
+ text.read_string
326
+ end
327
+
328
+ end
329
+ end
330
+ end
331
+ end
332
+ end
333
+