Created
April 14, 2012 11:26
-
-
Save billagee/2383726 to your computer and use it in GitHub Desktop.
Pure Ruby snippet that prints the name of the root (Desktop) MS UI Automation element using IUIAutomation::GetRootElement and wprintf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Find me at https://gist.github.com/2383726 | |
# | |
# This script shows how to use wprintf() and a few functions from the | |
# MS UI Automation COM interface (IUIAutomation) to print the name | |
# of the desktop's IUIAutomationElement on a Windows machine. | |
# For more information see the MS docs on: | |
# | |
# IUIAutomation::GetRootElement | |
# IUIAutomationElement::get_CurrentName | |
# | |
# For more info see: | |
# http://msdn.microsoft.com/en-us/library/windows/desktop/ee671406(v=vs.85).aspx | |
# | |
# \Program Files\Microsoft SDKs\Windows\v7.1\Include\UIAutomationClient.h | |
require 'rubygems' | |
require 'windows/com' | |
require 'windows/com/automation' | |
require 'windows/com/variant' | |
require 'windows/msvcrt/buffer' | |
require 'windows/msvcrt/string' | |
require 'windows/unicode' | |
require 'windows/error' | |
include Windows::COM | |
include Windows::COM::Automation | |
include Windows::COM::Variant | |
include Windows::MSVCRT::Buffer | |
include Windows::MSVCRT::String | |
include Windows::Unicode | |
include Windows::Error | |
# Convenience method for transforming a Ruby string to a wide-char C string | |
def get_wide_str_ptr(ruby_str) | |
# Get a wide-character version of the string | |
wide_str = multi_to_wide(ruby_str) | |
# Place the wide string in an array, and pack the array as a pointer to a | |
# null-terminated string | |
packed_str = [wide_str].pack('p') # Can also use 'p*' | |
# Get the memory address of the packed array, by unpacking it as an | |
# unsigned long: | |
packed_str.unpack('L').first # Can also use [0] | |
end | |
# Constants (from UIAutomationClient.h) | |
IID_CUIAutomation = [0xff48dba4, 0x60ef, 0x4201, 0xaa, 0x87, 0x54, 0x10, 0x3e, 0xef, 0x59, 0x4e].pack('LSSC8') | |
IID_IUIAutomation = [0x30cbe57d, 0xd9d0, 0x452a, 0xab, 0x13, 0x7a, 0xc5, 0xac, 0x48, 0x25, 0xee].pack('LSSC8') | |
UIA_NamePropertyId = 30005; | |
# Initialize COM | |
CoInitialize(nil) | |
# Instantiate the CUIAutomation object and store its IUIAutomation interface | |
# in iuia_ptr | |
# | |
# (See: http://rubyforge.org/pipermail/win32utils-devel/2008-June/001125.html) | |
iuia_ptr = 0.chr * 4 | |
hr = CoCreateInstance( | |
IID_CUIAutomation, | |
nil, | |
CLSCTX_INPROC_SERVER, | |
IID_IUIAutomation, | |
iuia_ptr | |
) | |
puts "HRESULT of CoCreateInstance is: " + hr.to_s | |
# Get a pointer to the iuia_ptr, which we'll use for the IUIAutomation functions | |
iuia_ptr_ptr = iuia_ptr.unpack('L').first | |
# The next thing to do is invoke the IUIAutomation::GetRootElement method, | |
# to get the desktop's automation element. To describe how to do that: | |
# | |
# - Get the virtual function table of the IUIAutomation object | |
# - Find the GetRootElement function in the table | |
# - Call GetRootElement | |
# | |
# For more info see these examples: | |
# http://rubyforge.org/forum/forum.php?thread_id=33188&forum_id=319 | |
# http://rubyforge.org/pipermail/win32utils-devel/2010-October/001624.html | |
# Allocate 4 bytes to store a pointer to the vtbl: | |
iuia_vtbl_ptr = 0.chr * 4 | |
# Allocate 4*58 bytes for the table, since there are 58 functions in the C | |
# interface's IUIAutomationVtbl struct (see UIAutomationClient.h). | |
iuia_table = 0.chr * (4 * 58) | |
# Make a copy of iuia_ptr that we can muck about with. | |
Memcpy.call(iuia_vtbl_ptr, iuia_ptr_ptr, 4) | |
# Copy the 4*58 bytes at the iuia_vtbl_ptr memory address to iuia_table | |
Memcpy.call(iuia_table, iuia_vtbl_ptr.unpack('L').first, 4 * 58) | |
# Unpack the contents of the virtual function table into the 'iuia_table' array. | |
iuia_table = iuia_table.unpack('L*') | |
puts "Number of elements in the vtbl is: " + iuia_table.length.to_s | |
# GetRootElement is the 6th function in the vtbl. Both the 'This' pointer | |
# and the out param must be specified as args, due to the C style interface: | |
GetRootElement = Win32::API::Function.new(iuia_table[5], 'PP', 'L') | |
# Call GetRootElement to get a pointer to the desktop IUIAutomationElement | |
desktop_ptr = 0.chr * 4 | |
hr = GetRootElement.call(iuia_ptr.unpack('L').first, desktop_ptr) | |
puts "HRESULT of GetRootElement is: " + hr.to_s | |
# To get the element's name, we need to call the get_CurrentName function. | |
# The function can be accessed through the IUIAutomationElement vtbl - and we | |
# can get the vtbl from the root element pointer. | |
# | |
# Allocate 4*85 bytes for the table, since there are 85 functions in the C | |
# interface's IUIAutomationElementVtbl struct (see UIAutomationClient.h). | |
element_vtbl_ptr = 0.chr * 4 | |
element_table = 0.chr * (4 * 85) | |
# Make a copy of desktop_ptr using its memory address | |
Memcpy.call(element_vtbl_ptr, desktop_ptr.unpack('L').first, 4) | |
# Unpack the contents of the virtual function table into the table array. | |
Memcpy.call(element_table, element_vtbl_ptr.unpack('L').first, 4 * 85) | |
element_table = element_table.unpack('L*') | |
puts "Number of elements in the vtbl is: " + element_table.length.to_s | |
# IUIAutomationElement::get_CurrentName is the 24th function in the vtbl. | |
Get_CurrentName = Win32::API::Function.new(element_table[23], 'PP', 'L') | |
# Create buffer for the BSTR that will receive the name from Get_CurrentName() | |
root_element_name = 0.chr * 4 | |
hr = Get_CurrentName.call(desktop_ptr.unpack('L').first, root_element_name) | |
puts hr | |
#puts get_last_error(hr) | |
# Print the name of the root element | |
Wprintf = Windows::API.new('wprintf', 'PP', 'I', 'msvcrt') | |
format_str = "The string is: '%s'\n" | |
Wprintf.call(get_wide_str_ptr(format_str), root_element_name.unpack('L').first) | |
CoUninitialize() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment