Chris Jansen
7 posts
|
This is code that I wrote to enumerate through the children of a parent window. I wrote it because of issues described in the FindWindowEx thread. I also created a class that actually recurses into each child to see if it has any children, but this wasn’t beneficial for my uses.
The class uses the EnumChildWindows Win32 API method, which takes a callback method as an argument. I’m using the win32-api from the Win32 Utils project (http://rubyforge.org/projects/win32utils), which supports callback methods for API method calls.
The ChildEnumerator class has two methods. enum_children just populates a hash with all of the children of a parent window. find_child looks for a specific child. Both methods have static versions. Note that my code has log4r statements in it, which you could easily replace with puts.
# Enumerate through the children of a window
class ChildEnumerator
include WindowsGui
attr_reader :children
# Define the EnumChildWindows API call.
@@EnumChildWindows = Win32::API.new('EnumChildWindows', 'LKP',
'L', 'user32')
def initialize
@children = Hash.new
end
# Static method to enumerate through children of the window_handle.
def self.enum_children( window_handle )
enumerator = ChildEnumerator.new
enumerator.enum_children( window_handle )
return enumerator.children
end
# Enumerate through children of the window_handle (static).
def enum_children( window_handle )
@children.clear
@log.debug("Finding children of handle #{window_handle}")
enum_proc = Win32::API::Callback.new('L', 'I'){ |winHandle|
@log.debug("Child #{winHandle}")
@children[winHandle] = Window.new(winHandle) if (@children[winHandle] == nil)
true
}
@@EnumChildWindows.call(window_handle, enum_proc, nil)
end
# Static method to look for the specified window text or class in the
# children of the window_handle.
def self.find_child( window_handle, id, search_class = true )
enumerator = ChildEnumerator.new
enumerator.find_child( window_handle, id, search_class )
end
# Look for the specified window text or class in the children
# of the window_handle.
def find_child( window_handle, id, search_class = true )
matching_child = 0
@log.debug("Searching for child #{id} in children of handle #{window_handle}")
enum_proc = Win32::API::Callback.new('L', 'I'){ |winHandle|
@log.debug("Child #{winHandle}")
class_name = ''
buffer = "\0" * 1024
# Look for a match on the text or class of the child control.
# length = get_window_text(winHandle, buffer, buffer.length)
length = send_with_buffer(winHandle, WM_GETTEXT, buffer.length, buffer)
text = (length == 0) ? '' : buffer[0..length -1]
if (id.gsub('_', '&').downcase == text.downcase)
# The control text matches
@log.debug(" MATCHED on text: #{text}")
matching_child = winHandle
elsif (search_class)
buffer = "\0" * 1024
length = get_class_name winHandle, buffer, buffer.length
class_name = (length == 0) ? '' : buffer[0..length -1]
if (id == class_name)
@log.debug(" MATCHED on class: #{class_name}")
# The control class matches
matching_child = winHandle
end
end
@log.debug(" No match, text: #{text}, class: #{class_name}") if (matching_child == 0)
# If a match is found, return false, which stops the enumeration
# of child windows.
(matching_child == 0)
}
@@EnumChildWindows.call(window_handle, enum_proc, nil)
@log.debug("Enumeration complete, matching_child=#{matching_child}")
matching_child # Return the matching child handle.
end
end
I integrated it into the child method of the Window class thusly:
when String
# Allow the user to use an underscore to specify an
# ampersand for the control name.
by_title = find_window_ex @handle, 0, nil, id.gsub('_', '&')
by_class = find_window_ex @handle, 0, id, nil
if (by_title == 0 && by_class == 0)
@log.debug("Did not find by title or class, recursing children")
result = ChildEnumerator.find_child(handle, id)
@log.debug("Found child #{result} for id #{id}") if (result > 0)
else
result = (by_title > 0) ? by_title : by_class
end
|
Ian Dees
35 posts
|
Nice! Thanks for posting this.
|