rautomation 0.6.3 → 0.7.0

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 (88) hide show
  1. data/.gitignore +34 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +24 -0
  4. data/History.rdoc +21 -0
  5. data/LICENSE +1 -1
  6. data/README.rdoc +8 -7
  7. data/Rakefile +22 -32
  8. data/VERSION +1 -1
  9. data/ext/UiaDll/Release/UiaDll.dll +0 -0
  10. data/ext/UiaDll/UiaDll.suo +0 -0
  11. data/ext/UiaDll/UiaDll/UiaDll.cpp +236 -10
  12. data/lib/rautomation/adapter/autoit/locators.rb +1 -1
  13. data/lib/rautomation/adapter/autoit/text_field.rb +10 -1
  14. data/lib/rautomation/adapter/autoit/window.rb +50 -23
  15. data/lib/rautomation/adapter/helper.rb +6 -6
  16. data/lib/rautomation/adapter/ms_uia.rb +22 -0
  17. data/lib/rautomation/adapter/{win_ffi → ms_uia}/button.rb +3 -2
  18. data/lib/rautomation/adapter/ms_uia/button_helper.rb +25 -0
  19. data/lib/rautomation/adapter/ms_uia/checkbox.rb +27 -0
  20. data/lib/rautomation/adapter/{win_ffi → ms_uia}/constants.rb +20 -5
  21. data/lib/rautomation/adapter/ms_uia/control.rb +180 -0
  22. data/lib/rautomation/adapter/{win_ffi → ms_uia}/functions.rb +24 -26
  23. data/lib/rautomation/adapter/ms_uia/keystroke_converter.rb +122 -0
  24. data/lib/rautomation/adapter/{win_ffi → ms_uia}/label.rb +3 -2
  25. data/lib/rautomation/adapter/ms_uia/list_box.rb +91 -0
  26. data/lib/rautomation/adapter/ms_uia/list_item.rb +49 -0
  27. data/lib/rautomation/adapter/ms_uia/locators.rb +23 -0
  28. data/lib/rautomation/adapter/{win_ffi → ms_uia}/radio.rb +2 -2
  29. data/lib/rautomation/adapter/{win_ffi → ms_uia}/select_list.rb +41 -7
  30. data/lib/rautomation/adapter/ms_uia/table.rb +86 -0
  31. data/lib/rautomation/adapter/{win_ffi → ms_uia}/text_field.rb +5 -4
  32. data/lib/rautomation/adapter/ms_uia/uia_dll.rb +62 -0
  33. data/lib/rautomation/adapter/ms_uia/window.rb +364 -0
  34. data/lib/rautomation/adapter/win_32.rb +21 -0
  35. data/lib/rautomation/adapter/win_32/button.rb +14 -0
  36. data/lib/rautomation/adapter/{win_ffi → win_32}/button_helper.rb +1 -1
  37. data/lib/rautomation/adapter/win_32/checkbox.rb +14 -0
  38. data/lib/rautomation/adapter/win_32/constants.rb +68 -0
  39. data/lib/rautomation/adapter/{win_ffi → win_32}/control.rb +30 -23
  40. data/lib/rautomation/adapter/win_32/functions.rb +313 -0
  41. data/lib/rautomation/adapter/win_32/keys.rb +121 -0
  42. data/lib/rautomation/adapter/win_32/label.rb +10 -0
  43. data/lib/rautomation/adapter/win_32/list_box.rb +40 -0
  44. data/lib/rautomation/adapter/{win_ffi → win_32}/locators.rb +2 -2
  45. data/lib/rautomation/adapter/win_32/radio.rb +11 -0
  46. data/lib/rautomation/adapter/win_32/select_list.rb +97 -0
  47. data/lib/rautomation/adapter/win_32/table.rb +22 -0
  48. data/lib/rautomation/adapter/win_32/text_field.rb +42 -0
  49. data/lib/rautomation/adapter/{win_ffi → win_32}/window.rb +79 -62
  50. data/lib/rautomation/element_collections.rb +9 -1
  51. data/lib/rautomation/window.rb +17 -9
  52. data/rautomation.gemspec +24 -0
  53. data/spec/adapter/autoit/window_spec.rb +71 -0
  54. data/spec/adapter/{win_ffi → ms_uia}/button_spec.rb +1 -1
  55. data/spec/adapter/{win_ffi → ms_uia}/checkbox_spec.rb +9 -3
  56. data/spec/adapter/ms_uia/control_spec.rb +28 -0
  57. data/spec/adapter/ms_uia/keystroke_converter_spec.rb +50 -0
  58. data/spec/adapter/{win_ffi → ms_uia}/label_spec.rb +2 -2
  59. data/spec/adapter/ms_uia/list_item_spec.rb +14 -0
  60. data/spec/adapter/{win_ffi → ms_uia}/listbox_spec.rb +18 -5
  61. data/spec/adapter/{win_ffi → ms_uia}/radio_spec.rb +3 -1
  62. data/spec/adapter/ms_uia/select_list_spec.rb +109 -0
  63. data/spec/adapter/{win_ffi → ms_uia}/table_spec.rb +12 -1
  64. data/spec/adapter/{win_ffi → ms_uia}/text_field_spec.rb +2 -1
  65. data/spec/adapter/ms_uia/window_spec.rb +89 -0
  66. data/spec/adapter/win_32/button_spec.rb +30 -0
  67. data/spec/adapter/win_32/checkbox_spec.rb +48 -0
  68. data/spec/adapter/win_32/label_spec.rb +15 -0
  69. data/spec/adapter/win_32/listbox_spec.rb +42 -0
  70. data/spec/adapter/win_32/radio_spec.rb +32 -0
  71. data/spec/adapter/{win_ffi → win_32}/select_list_spec.rb +16 -16
  72. data/spec/adapter/win_32/table_spec.rb +28 -0
  73. data/spec/adapter/win_32/text_field_spec.rb +24 -0
  74. data/spec/adapter/{win_ffi → win_32}/window_spec.rb +19 -14
  75. data/spec/button_spec.rb +1 -0
  76. data/spec/buttons_spec.rb +4 -4
  77. data/spec/spec_helper.rb +30 -4
  78. data/spec/text_field_spec.rb +6 -7
  79. data/spec/window_spec.rb +12 -0
  80. data/spec/windows_spec.rb +19 -0
  81. metadata +116 -97
  82. data/lib/rautomation/adapter/win_ffi.rb +0 -21
  83. data/lib/rautomation/adapter/win_ffi/checkbox.rb +0 -19
  84. data/lib/rautomation/adapter/win_ffi/keystroke_converter.rb +0 -67
  85. data/lib/rautomation/adapter/win_ffi/list_box.rb +0 -60
  86. data/lib/rautomation/adapter/win_ffi/ms_uia/uia_dll.rb +0 -36
  87. data/lib/rautomation/adapter/win_ffi/table.rb +0 -57
  88. data/spec/adapter/win_ffi/keystroke_converter_spec.rb +0 -47
@@ -1,6 +1,9 @@
1
+ #todo - replace as much of this as possible with UIA versions
2
+
3
+
1
4
  module RAutomation
2
5
  module Adapter
3
- module WinFfi
6
+ module MsUia
4
7
  # @private
5
8
  module Functions
6
9
  extend FFI::Library
@@ -128,20 +131,6 @@ module RAutomation
128
131
  locators
129
132
  end
130
133
 
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
134
  def window_pid(hwnd)
146
135
  pid = FFI::MemoryPointer.new :int
147
136
  window_thread_process_id(hwnd, pid)
@@ -180,16 +169,25 @@ module RAutomation
180
169
  end
181
170
 
182
171
  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
172
+ case
173
+ when locators[:id]
174
+ uia_window = UiaDll::element_from_handle(window_hwnd) # finds IUIAutomationElement for given parent window
175
+ uia_control = UiaDll::find_child_by_id(uia_window, locators[:id].to_s)
176
+ hwnd = UiaDll::current_native_window_handle(uia_control) # return HWND of UIA element
177
+ raise UnknownElementException, "#{locators[:id]} does not exist" if hwnd == 0
178
+ hwnd
179
+ when locators[:point]
180
+ uia_control = UiaDll::element_from_point(locators[:point][0], locators[:point][1])
181
+ hwnd = UiaDll::current_native_window_handle(uia_control) # return HWND of UIA element
182
+ raise UnknownElementException, "#{locators[:point]} does not exist" if hwnd == 0
183
+ hwnd
184
+ else
185
+ hwnd = find_hwnd(locators, window_hwnd) do |hwnd|
186
+ locators_match?(locators, control_properties(hwnd, locators))
187
+ end
188
+
189
+ raise UnknownElementException, "Element with #{locators.inspect} does not exist" if (hwnd == 0) or (hwnd == nil)
190
+ hwnd
193
191
  end
194
192
  end
195
193
 
@@ -243,7 +241,7 @@ module RAutomation
243
241
  end
244
242
 
245
243
  def retrieve_table_strings_for_row(control_hwnd, row)
246
- hModule = load_library("oleacc.dll") # TODO should be done only one time
244
+ hModule = load_library("oleacc.dll") # TODO should be done only one time
247
245
 
248
246
  strings_ptr = FFI::MemoryPointer.new :pointer
249
247
  columns_ptr = FFI::MemoryPointer.new :pointer
@@ -0,0 +1,122 @@
1
+ #todo - common file, extract it
2
+
3
+ module RAutomation
4
+ module Adapter
5
+ module MsUia
6
+ class KeystrokeConverter
7
+ class << self
8
+ def convert(str)
9
+ special_characters = ""
10
+
11
+ str.split(/([{}])/).inject([]) do |converted_keys, str|
12
+ if str == "}"
13
+ converted_keys << convert_special_characters(special_characters << str)
14
+ special_characters = ""
15
+ elsif str == "{" || !special_characters.empty?
16
+ special_characters << str
17
+ else
18
+ converted_keys += convert_characters(str)
19
+ end
20
+ converted_keys
21
+ end.flatten
22
+ end
23
+
24
+ private
25
+
26
+ def convert_special_characters chars
27
+ case chars.downcase
28
+ when "{tab}"
29
+ Constants::VK_TAB
30
+ when "{backspace}"
31
+ Constants::VK_BACK
32
+ when "{enter}"
33
+ Constants::VK_RETURN
34
+ when "{left}"
35
+ Constants::VK_LEFT
36
+ when "{right}"
37
+ Constants::VK_RIGHT
38
+ when "{down}"
39
+ Constants::VK_DOWN
40
+ when "{up}"
41
+ Constants::VK_UP
42
+ when "{home}"
43
+ Constants::VK_HOME
44
+ when "{end}"
45
+ Constants::VK_END
46
+ when "{delete}"
47
+ Constants::VK_DELETE
48
+ when "{pgdown}"
49
+ Constants::VK_NEXT
50
+ when "{pgup}"
51
+ Constants::VK_PRIOR
52
+ else
53
+ # unsupported special tag, ignore the tag itself, but convert the
54
+ # characters inside the tag
55
+ convert_characters(chars.gsub(/[{}]/, ""))
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def convert_characters(element)
62
+ element.split(//).inject([]) do |chars, char|
63
+ char_code = char.upcase.unpack("c")[0]
64
+ case char
65
+ when "!"
66
+ chars += in_upcase(49)
67
+ when "@"
68
+ chars += in_upcase(50)
69
+ when "\#"
70
+ chars += in_upcase(51)
71
+ when "$"
72
+ chars += in_upcase(52)
73
+ when "%"
74
+ chars += in_upcase(53)
75
+ when "^"
76
+ chars += in_upcase(54)
77
+ when "&"
78
+ chars += in_upcase(55)
79
+ when "*"
80
+ chars += in_upcase(56)
81
+ when "("
82
+ chars += in_upcase(57)
83
+ when ")"
84
+ chars += in_upcase(48)
85
+ when "\""
86
+ chars += in_upcase(0xDE)
87
+ when "'"
88
+ chars << 0xDE
89
+ when "/"
90
+ chars << 0xBF
91
+ when "-"
92
+ chars << 0xBD
93
+ when ","
94
+ chars << 0xBC
95
+ when "'"
96
+ chars << 0xDE
97
+ when "&"
98
+ chars += in_upcase(0x37)
99
+ when "_"
100
+ chars += in_upcase(0xBD)
101
+ when "<"
102
+ chars += in_upcase(0xBC)
103
+ when ">"
104
+ chars += in_upcase(0xBE)
105
+ else
106
+ if char =~ /[A-Z]/
107
+ chars += in_upcase(char_code)
108
+ else
109
+ chars << char_code
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ def in_upcase(char_code)
116
+ [Constants::VK_LSHIFT, char_code]
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -1,16 +1,17 @@
1
1
  module RAutomation
2
2
  module Adapter
3
- module WinFfi
3
+ module MsUia
4
4
  class Label < Control
5
5
  include WaitHelper
6
6
  include Locators
7
7
 
8
+ #todo - replace with UIA version
8
9
  def value
9
10
  Functions.control_value(Functions.control_hwnd(@window.hwnd, @locators))
10
11
  end
11
12
 
12
13
  def exist?
13
- @locators[:id].nil? ? super : super && matches_type(Constants::UIA_LABEL_CONTROL_TYPE)
14
+ super && matches_type?(Constants::UIA_TEXT_CONTROL_TYPE)
14
15
  end
15
16
 
16
17
  alias_method :exists?, :exist?
@@ -0,0 +1,91 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module MsUia
4
+
5
+ class ListBox < Control
6
+ include WaitHelper
7
+ include Locators
8
+
9
+ def count
10
+ UiaDll::find_children(uia_element, nil)
11
+ end
12
+
13
+ def items
14
+ list_items = []
15
+ children = FFI::MemoryPointer.new :pointer, self.count
16
+ length = UiaDll::find_children(uia_element, children)
17
+
18
+ children.read_array_of_pointer(length).each do |child|
19
+ if (UiaDll::current_control_type(child) == Constants::UIA_LIST_ITEM_CONTROL_TYPE) or (UiaDll::current_control_type(child) == Constants::UIA_DATA_ITEM_CONTROL_TYPE)
20
+ child_name = FFI::MemoryPointer.new :char, UiaDll::get_name(child, nil) + 1
21
+ UiaDll::get_name(child, child_name)
22
+ list_items.push(@window.list_item(:value => child_name.read_string))
23
+ end
24
+ end
25
+
26
+ list_items
27
+ end
28
+
29
+ def strings
30
+ items.collect { |item| item.value}
31
+ end
32
+
33
+ def value
34
+ count.times do |index|
35
+ if selected?(index)
36
+ return strings[index]
37
+ end
38
+ end
39
+
40
+ ""
41
+ end
42
+
43
+ def exist?
44
+ super && matches_type?(Constants::UIA_LIST_CONTROL_TYPE)
45
+ end
46
+
47
+ alias_method :exists?, :exist?
48
+
49
+ def selected?(index)
50
+ if items[index]
51
+ return items[index].selected?
52
+ end
53
+
54
+ false
55
+ end
56
+
57
+ def select(index)
58
+ children = FFI::MemoryPointer.new :pointer, self.count
59
+
60
+ length = UiaDll::find_children(uia_element, children)
61
+
62
+ target_element = children.read_array_of_pointer(length)[index]
63
+
64
+ UiaDll::select(target_element)
65
+ end
66
+
67
+ def list_boundary
68
+ boundary = FFI::MemoryPointer.new :long, 4
69
+
70
+ Functions.send_message(hwnd, Constants::LB_GETITEMRECT, 0 ,boundary)
71
+
72
+ boundary.read_array_of_long(4)
73
+ end
74
+
75
+ def get_top_index
76
+ Functions.send_message(hwnd, Constants::LB_GETTOPINDEX, 0 ,nil)
77
+ end
78
+
79
+ def list_item_height
80
+ Functions.send_message(hwnd, Constants::LB_GETITEMHEIGHT, 0 ,nil)
81
+ end
82
+
83
+ def scroll_to_item(row)
84
+ Functions.send_message(hwnd, Constants::LB_SETTOPINDEX, row ,nil)
85
+ end
86
+
87
+ end
88
+ end
89
+ end
90
+ end
91
+
@@ -0,0 +1,49 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module MsUia
4
+ class ListItem < Control
5
+ include WaitHelper
6
+ include Locators
7
+
8
+ #Can't get by value without a handle or a bug fix in the new way of getting a control by value so this is a workaround
9
+ def uia_element
10
+ if @locators[:value]
11
+ uia_window = UiaDll::element_from_handle(@window.hwnd)
12
+ begin
13
+ uia_window.read_pointer
14
+ rescue FFI::NullPointerError => e
15
+ raise UnknownElementException, "Window with handle #{@window.hwnd} does not exist"
16
+ end
17
+ uia_control = UiaDll::find_child_by_name(uia_window, @locators[:value].to_s)
18
+ begin
19
+ uia_control.read_pointer
20
+ rescue FFI::NullPointerError => e
21
+ raise UnknownElementException, "#{@locators[:value]} does not exist"
22
+ end
23
+ uia_control
24
+ else
25
+ super
26
+ end
27
+ end
28
+
29
+ def value
30
+ list_item = uia_element
31
+ item_value = FFI::MemoryPointer.new :char, UiaDll::get_name(list_item, nil) + 1
32
+ UiaDll::get_name(list_item, item_value)
33
+ item_value.read_string
34
+ end
35
+
36
+ def exist?
37
+ super && matches_type?(Constants::UIA_LIST_ITEM_CONTROL_TYPE)
38
+ end
39
+
40
+ def selected?
41
+ UiaDll::get_is_selected(uia_element)
42
+ end
43
+
44
+ alias_method :exists?, :exist?
45
+
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,23 @@
1
+ #todo - some files are common between Win32 and MsUia adapter, consider pulling out to shared location
2
+
3
+ module RAutomation
4
+ module Adapter
5
+ module MsUia
6
+ module Locators
7
+
8
+ private
9
+
10
+ def extract(locators)
11
+ # windows locators
12
+ @hwnd = locators[:hwnd].to_i if locators[:hwnd]
13
+ locators[:pid] = locators[:pid].to_i if locators[:pid]
14
+ locators[:index] = locators[:index].to_i if locators[:index]
15
+
16
+ # control locator
17
+ locators = self.class::DEFAULT_LOCATORS.merge(locators) if self.class.const_defined?(:DEFAULT_LOCATORS)
18
+ @locators = {:index => 0}.merge locators
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,6 +1,6 @@
1
1
  module RAutomation
2
2
  module Adapter
3
- module WinFfi
3
+ module MsUia
4
4
  class Radio < Control
5
5
  include WaitHelper
6
6
  include Locators
@@ -8,7 +8,7 @@ module RAutomation
8
8
 
9
9
 
10
10
  def exist?
11
- @locators[:id].nil? ? super : super && matches_type(Constants::UIA_RADIO_BUTTON_CONTROL_TYPE)
11
+ super && matches_type?(Constants::UIA_RADIO_BUTTON_CONTROL_TYPE)
12
12
  end
13
13
 
14
14
  alias_method :exists?, :exist?
@@ -1,6 +1,6 @@
1
1
  module RAutomation
2
2
  module Adapter
3
- module WinFfi
3
+ module MsUia
4
4
 
5
5
  class SelectList < Control
6
6
  include WaitHelper
@@ -11,16 +11,18 @@ module RAutomation
11
11
 
12
12
  def initialize(select_list, text, index)
13
13
  @select_list = select_list
14
- @text = text
15
- @index = index
14
+ @text = text
15
+ @index = index
16
16
  end
17
17
 
18
+ #todo - replace with UIA version
18
19
  def selected?
19
20
  selected_idx = Functions.send_message(@select_list.control_hwnd, Constants::CB_GETCURSEL, 0, nil)
20
21
  return false if selected_idx == Constants::CB_ERR
21
22
  @text == Functions.retrieve_combobox_item_text(@select_list.control_hwnd, selected_idx)
22
23
  end
23
24
 
25
+ #todo - replace with UIA version
24
26
  def select
25
27
  @select_list.assert_enabled
26
28
  Functions.send_message(@select_list.control_hwnd, Constants::CB_SETCURSEL, @index, nil) != Constants::CB_ERR
@@ -29,18 +31,25 @@ module RAutomation
29
31
  alias_method :set, :select
30
32
  end
31
33
 
34
+ #todo - replace with UIA version
32
35
  def initialize(window, locators)
33
36
  super
34
37
  @hwnd = Functions.control_hwnd(@window.hwnd, @locators)
35
38
  end
36
39
 
40
+ def set(value)
41
+ list = UiaDll::element_from_handle(@hwnd)
42
+ UiaDll::set_value(list, value)
43
+ end
44
+
45
+ #todo - replace with UIA version
37
46
  def options(options = {})
38
47
  items = []
39
48
 
40
49
  item_count.times do |item_no|
41
50
  item = Functions.retrieve_combobox_item_text(@hwnd, item_no)
42
51
 
43
- if options[:text]
52
+ if options[:text]
44
53
  items.push(SelectListOption.new(self, item, item_no)) if options[:text] == item
45
54
  else
46
55
  items.push(SelectListOption.new(self, item, item_no))
@@ -51,10 +60,11 @@ module RAutomation
51
60
  end
52
61
 
53
62
  def value
54
- selected_option = options.find {|option| option.selected?}
63
+ selected_option = options.find { |option| option.selected? }
55
64
  selected_option ? selected_option.text : ""
56
65
  end
57
66
 
67
+ #todo - replace with UIA version
58
68
  def option(options)
59
69
  item_count.times do |item_no|
60
70
  item = Functions.retrieve_combobox_item_text(@hwnd, item_no)
@@ -68,19 +78,43 @@ module RAutomation
68
78
  @hwnd
69
79
  end
70
80
 
81
+ def select(index)
82
+ Functions.send_message(@hwnd, Constants::CB_SETCURSEL, index, nil) != Constants::CB_ERR
83
+ end
84
+
85
+ def list_item_height
86
+ Functions.send_message(@hwnd, Constants::CB_GETITEMHEIGHT, 0 ,nil)
87
+ end
88
+
89
+ def dropbox_boundary
90
+ boundary = FFI::MemoryPointer.new :long, 4
91
+
92
+ Functions.send_message(@hwnd, Constants::CB_GETDROPPEDCONTROLRECT, 0 ,boundary)
93
+
94
+ boundary.read_array_of_long(4)
95
+ end
96
+
97
+ def get_top_index
98
+ Functions.send_message(@hwnd, Constants::CB_GETTOPINDEX, 0 ,nil)
99
+ end
100
+
101
+ def scroll_to_item(row)
102
+ Functions.send_message(@hwnd, Constants::CB_SETTOPINDEX , row ,nil)
103
+ end
104
+
71
105
  def exist?
72
- @locators[:id].nil? ? super : super && matches_type(Constants::UIA_COMBOBOX_CONTROL_TYPE)
106
+ super && matches_type?(Constants::UIA_COMBOBOX_CONTROL_TYPE)
73
107
  end
74
108
 
75
109
  alias_method :exists?, :exist?
76
110
 
77
111
  private
78
112
 
113
+ #todo - replace with UIA version
79
114
  def item_count
80
115
  Functions.send_message(@hwnd, Constants::CB_GETCOUNT, 0, nil)
81
116
  end
82
117
 
83
-
84
118
  end
85
119
  end
86
120
  end