lib/watir/winClicker.rb in watir-1.6.5 vs lib/watir/winClicker.rb in watir-1.6.6.rc1

- old
+ new

@@ -1,496 +1,496 @@ -=begin - license - --------------------------------------------------------------------------- - Copyright (c) 2004-2006, Paul Rogers and Bret Pettichord - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the names Paul Rogers, nor Bret Pettichord nor the names of any - other contributors to this software may be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS - IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -------------------------------------------------------------------------- - (based on BSD Open Source License) -=end - -=begin rdoc - This is Watir's window clicker helper class, uses Win32 api - calls to access buttons and windows. - - Typical usage: - # include this file in your script - require "watir/winClicker.rb" - - # create a new instance of WinClicker and use it - wc = WinClicker.new - wc.clickWindowsButton("My Window", "Click Me", 30) - -=end - -# -# class to click javascript dialog boxes, file requester dialogs etc -require 'dl/import' -require 'dl/struct' -require "timeout" -require 'Win32API' - -class WinClicker - - WM_CLOSE = 0x0010 - WM_KEYDOWN = 0x0100 - WM_KEYUP = 0x0101 - WM_CHAR = 0x0102 - BM_CLICK = 0x00F5 - WM_COMMAND = 0x0111 - WM_SETTEXT = 0x000C - WM_GETTEXT = 0x000D - - HWND_TOP = 0 - HWND_BOTTOM = 1 - HWND_TOPMOST = -1 - HWND_NOTOPMOST = -2 - - SWP_SHOWWINDOW = 0x40 - SWP_NOSIZE = 1 - SWP_NOMOVE = 2 - - TRUE_1 = 1 - - # these are constants for commonly used windows windows - WINCLASS_DIALOG = "32770" - - # these are the most used methods - - def initialize - @User32 = DL.dlopen("user32") - # we must determine the path we are in - @path_to_clicker = '"' + File.expand_path(File.dirname(__FILE__)) + '"' - end - - - # The system function passes command to the command interpreter, which executes the string as an operating-system command - # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/html/_crt_system.2c_._wsystem.asp - # using win32api - def winsystem(command) - pid = Win32API.new("crtdll", "system", ['P'], 'L').Call(command) - end - - # returns the short path version of a long path - # 8.3 style - def getShortFileName(longName) - size = 255 - buffer = " " * 255 - returnSize = Win32API.new("kernel32" , "GetShortPathNameA" , 'ppl' , 'L').Call(longName , buffer , size ) - a = "" - a = a + buffer[0...returnSize] - return a - end - - # Set the first edit box in the Choose file dialog to textToSet - # we may need to play with the default try count. 3 is a reasonably safe value. - def setFileRequesterFileName( textToSet, tryCount = 3 ) - for i in (1..tryCount) - # first set the Choose File Window to be active - hWnd = getWindowHandle("Choose file" ) - if hWnd != -1 - makeWindowActive(hWnd) - setTextValueForFileNameField( hWnd , textToSet) - clickWindowsButton_hwnd(hWnd, "&Open") - return true - end - end - return false - end - - # fire off setting the file name for the Choose file dialog - # in a new process - def setFileRequesterFileName_newProcess ( textToSet ) - myapp = "rubyw #{@path_to_clicker}/setFileDialog.rb #{textToSet}" - # first argument to system call is a window title, in this case blank "" - winsystem( "start \"\" #{myapp}" ) - end - - # Return the text value from the first combo box - # on the Choose file dialog or nil if not found - def getFileRequesterFileName() - # first set the Choose File Window to be active - hWnd = getWindowHandle("Choose file" ) - if hWnd != -1 - makeWindowActive(hWnd) - return getTextValueForFileNameField( hWnd ) - else - return nil - end - end - - # Click on a dialog with title of "Internet Explorer" - # Default button to click is "OK" - # parenthWnd not used - def clickJavaScriptDialog(button="OK" , parenthWnd = -1) - clickWindowsButton("Internet Explorer" , button ) - end - - # Calls system to launch a new process to click on the button - # defaults to "OK" button - def clickJSDialog_NewProcess(button = "OK" ) - myapp = "rubyw #{@path_to_clicker}/clickJSDialog.rb #{button}" - log "Starting win clicker in a new process. Looking for button #{button}" - log "Starting app: #{myapp}" - # first argument to system call is a window title, in this case blank "" - winsystem( "start \"\" #{myapp}" ) - end - - - # as a thread - def clickJSDialog_Thread(button = "OK" ) - sleep 3 - n = 0 - while n < 3 - sleep 1 - clickWindowsButton("Internet Explorer" , button ) - n=n+1 - end - end - - # Looks for a window titled "Security Alert", clicks - # on Yes button - def clearSecurityAlertBox - clickWindowsButton("Security Alert" , "&Yes" ) - end - alias :clear_security_alert :clearSecurityAlertBox - - # Returns the parent handle for the given child handle - def getParent (childhWnd ) - # pass a hWnd into this function and it will return the parent hWnd - getParentWindow = @User32['GetParent' , 'II' ] - a , b = getParentWindow.call(childhWnd ) - return a - end - alias :get_parent :getParent - - def with_dl_callback(type, prc) - callback = DL.callback(type, &prc) - error = nil - begin - yield callback - ensure - DL.remove_callback(callback) - end - end - - # Enumerates open windows and - # returns a window handle from a given title and window class - # Window class and title are matched regexes - def getWindowHandle(title, winclass = "" ) - enum_windows = @User32['EnumWindows', 'IPL'] - get_class_name = @User32['GetClassName', 'ILpI'] - get_caption_length = @User32['GetWindowTextLengthA' ,'LI' ] # format here - return value type (Long) followed by parameter types - int in this case - see http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/~checkout~/ruby/ext/dl/doc/dl.txt? - get_caption = @User32['GetWindowTextA', 'iLsL' ] - - len = 32 - buff = " " * len - classMatch = false - - bContinueEnum = -1 # Windows "true" to continue enum_windows. - found_hwnd = -1 - - enum_windows_proc = lambda {|hwnd,lparam| - sleep 0.05 - r,rs = get_class_name.call(hwnd, buff, buff.size) - - if winclass != "" then - if /#{winclass}/ =~ rs[1].to_s - classMatch = true - end - else - classMatch = true - end - - if classMatch ==true - textLength, a = get_caption_length.call(hwnd) - captionBuffer = " " * (textLength+1) - t , textCaption = get_caption.call(hwnd, captionBuffer , textLength+1) - if /#{title}/ =~ textCaption[1].to_s - found_hwnd = hwnd - bContinueEnum = 0 # False, discontinue enum_windows - end - bContinueEnum - else - bContinueEnum - end - } - with_dl_callback('ILL',enum_windows_proc) do |callback| - r,rs = enum_windows.call(callback, 0) - end - return found_hwnd - end - alias :get_window_handle :getWindowHandle - - # Call SwitchToThisWindow win32api which will - # The SwitchToThisWindow function is called to switch focus to a specified window - # and bring it to the foreground - def makeWindowActive (hWnd) - switch_to_window = @User32['SwitchToThisWindow' , 'pLI' ] - # set it to be the one with focus - switch_to_window.call(hWnd , 1) - end - alias :make_window_active :makeWindowActive - - # Posts a message to the handle passed in to click - def clickButtonWithHandle(buttonhWnd) - post_message = @User32['PostMessage', 'ILILL'] - r,rs = post_message.call(buttonhWnd, BM_CLICK, 0, 0) - end - alias :click_button_with_handle :clickButtonWithHandle - - # Based on the parent window handle passed in, - # click on the button with the given caption. - def clickWindowsButton_hwnd (hwnd , buttonCaption ) - makeWindowActive(hwnd) - d = getChildHandle( hwnd , buttonCaption ) - if d != -1 - makeWindowActive(hwnd) - clickButtonWithHandle(d) - else - return false - end - return true - end - alias :click_windows_button_hwnd :clickWindowsButton_hwnd - - # this clicks the button with the name in the window with the caption. It keeps looking for the button until - # until the timeout expires - def clickWindowsButton (windowCaption , buttonCaption , maxWaitTime=30 ) - sleep 1 - hwnd = -1 - begin - timeout(maxWaitTime) do - hwnd = getWindowHandle(windowCaption) - while hwnd == -1 - hwnd = getWindowHandle(windowCaption) - sleep 0.5 - end - makeWindowActive(hwnd) - end - rescue Timeout::Error - return false - rescue => e - raise e - end - if hwnd != -1 - makeWindowActive(hwnd) - else - end - d = getChildHandle( hwnd , buttonCaption ) - if d != -1 - makeWindowActive(hwnd) - clickButtonWithHandle(d) - else - return false - end - return true - end - alias :click_windows_button :clickWindowsButton - - # Enumerate through children of the parent hwnd, pass back - # the handle for the control with the given caption - # the caption is compared as a regex - def getChildHandle ( hWnd , childCaption ) - enum_childWindows = @User32['EnumChildWindows' , 'IIPL' ] - get_caption_length = @User32['GetWindowTextLengthA' ,'LI' ] # format here - return value type (Long) followed by parameter types - int in this case - see http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/~checkout~/ruby/ext/dl/doc/dl.txt? - get_caption = @User32['GetWindowTextA', 'iLsL' ] - match_hwnd = -1 # hWnd of handle matching childCaption - buff = " " * 16 - get_class_name = @User32['GetClassName', 'ILpI'] - - bContinueEnum = -1 - enum_childWindowsProc = lambda {|chwnd,lparam| - r,rs = get_class_name.call(chwnd, buff, buff.size) - textLength, a = get_caption_length.call(chwnd) - captionBuffer = " " * (textLength+1) - - t , textCaption = get_caption.call(chwnd, captionBuffer , textLength+1) - if /#{childCaption}/ =~ textCaption[1].to_s then - match_hwnd = chwnd - bContinueEnum = 0 # Windows "false" to discontinue enum_childWindow - end - bContinueEnum - } - with_dl_callback('ILL',enum_childWindowsProc) do |callback| - r = enum_childWindows.call(hWnd, callback ,0) - end - return match_hwnd - end - alias :get_chwnd :getChildHandle - - # Convenience method to return Static text for - # children of the window with the given caption - def getStaticText(caption) - return getStaticTextFromWindow(caption, -1) - end - alias :get_static_text :getStaticText - - # Convenience method to return Static text for - # children of the window handle - def getStaticText_hWnd (hWnd) - return getStaticTextFromWindow("" , hWnd) - end - alias :get_static_text_hwnd :getStaticText_hWnd - - # Return text as an array from child controls of the window - # given as either a handle or with the given caption - # that have a class type of Static - def getStaticTextFromWindow( windowCaption , hWnd) - enum_childWindows = @User32['EnumChildWindows' , 'IIPL' ] - get_caption_length = @User32['GetWindowTextLengthA' ,'LI' ] # format here - return value type (Long) followed by parameter types - int in this case - see http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/~checkout~/ruby/ext/dl/doc/dl.txt? - get_caption = @User32['GetWindowTextA', 'iLsL' ] - - staticText = [] - buff = " " * 16 - get_class_name = @User32['GetClassName', 'ILpI'] - - if hWnd == -1 - hWnd = getWindowHandle(windowCaption) - end - - if hWnd == -1 - return staticText - end - - bContinueEnum = -1 - enum_childWindows_proc = lambda {|hWnd,lparam| - r,rs = get_class_name.call(hWnd, buff, buff.size) - if rs[1].to_s == "Static" # there must be a better way of detecting this - textLength, a = get_caption_length.call(hWnd) - captionBuffer = " " * (textLength+1) - t , textCaption = get_caption.call(hWnd, captionBuffer , textLength+1) - staticText << textCaption[1].to_s - end - bContinueEnum - } - with_dl_callback('ILL',enum_childWindows_proc) do |callback| - r = enum_childWindows.call(hWnd, callback ,0) - end - return staticText - end - alias :get_static_text_from_window :getStaticTextFromWindow - - # returns the handle (or -1 if its not found) of the - # nth control of this class in the parent window specified - # by the window handle - def getHandleOfControl (hWnd , controlClass, position ) - enum_childWindows = @User32['EnumChildWindows' , 'IIPL' ] - get_caption_length = @User32['GetWindowTextLengthA' ,'LI' ] # format here - return value type (Long) followed by parameter types - int in this case - see http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/~checkout~/ruby/ext/dl/doc/dl.txt? - get_caption = @User32['GetWindowTextA', 'iLsL' ] - control_hWnd = [] - buff = " " * 16 - get_class_name = @User32['GetClassName', 'ILpI'] - - bContinueEnum = -1 - enum_childWindows_proc = lambda {|hWnd,lparam| - r,rs = get_class_name.call(hWnd, buff, buff.size) - if rs[1].to_s == controlClass # there must be a better way of detecting this - control_hWnd << hWnd - end - bContinueEnum - } - with_dl_callback('ILL',enum_childWindows_proc) do |callback| - r = enum_childWindows.call(hWnd, callback ,0) - end - controlHwnd = control_hWnd[position] - if controlHwnd == nil then - controlHwnd = -1 - end - return controlHwnd - end - alias :get_handle_of_ctrl :getHandleOfControl - - # Call set text on the given window handle - def setComboBoxText(hWnd , textToSet) - set_text(hWnd, textToSet) - end - alias :set_combo_txt :setComboBoxText - - # Call set text on the given window handle - def setTextBoxText(hWnd , textToSet) - set_text(hWnd, textToSet) - end - alias :set_textbox_txt :setTextBoxText - - # Private method to set text called by the two methods above - def set_text(hWnd, textToSet) - send_message = @User32['SendMessage', 'ILISS'] - r ,rs = send_message.call(hWnd , WM_SETTEXT ,'',textToSet) - end - private :set_text - - # Get the text in the handle for the given control - def getControlText(hWnd) - buff = " " * 256 - send_message = @User32['SendMessage', 'ILIIS'] - r ,rs = send_message.call(hWnd , WM_GETTEXT , 256 , buff ) - return buff.to_s - end - alias :get_ctrl_txt :getControlText - - # get the title for the specified hwnd - def getWindowTitle(hWnd) - buff = " " * 256 - getWindowText = @User32['GetWindowText' , 'ILSI'] - r , rs = getWindowText.call( hWnd , buff , 256 ) - return buff.to_s - end - alias :get_win_title :getWindowTitle - - # Get the text in the first combo box - # file requester methods returns nil on failure to - # locate the 1st combobox - def getTextValueForFileNameField(parenthWnd) - f = getHandleOfControl(parenthWnd, "ComboBox", 1) - if f == -1 then - # unable to find the first combobox - return nil - else - # we have the control and now - # can send it some messages - return getWinText(f ) - end - end - alias :get_file_name :getTextValueForFileNameField - - # this sets the filename field to text to set - def setTextValueForFileNameField( parenthWnd , textToSet ) - # get the handle of the nth control that is an Edit box - f = getHandleOfControl(parenthWnd, "Edit" , 0 ) - if f == -1 then - # unable to get a handle on the first edit control - return false - else - # we found the control and can now send it some messages - setComboBoxText(f , textToSet) - return true - end - end - alias :set_file_name :setTextValueForFileNameField -end +=begin + license + --------------------------------------------------------------------------- + Copyright (c) 2004-2006, Paul Rogers and Bret Pettichord + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the names Paul Rogers, nor Bret Pettichord nor the names of any + other contributors to this software may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + -------------------------------------------------------------------------- + (based on BSD Open Source License) +=end + +=begin rdoc + This is Watir's window clicker helper class, uses Win32 api + calls to access buttons and windows. + + Typical usage: + # include this file in your script + require "watir/winClicker.rb" + + # create a new instance of WinClicker and use it + wc = WinClicker.new + wc.clickWindowsButton("My Window", "Click Me", 30) + +=end + +# +# class to click javascript dialog boxes, file requester dialogs etc +require 'dl/import' +require 'dl/struct' +require "timeout" +require 'Win32API' + +class WinClicker + + WM_CLOSE = 0x0010 + WM_KEYDOWN = 0x0100 + WM_KEYUP = 0x0101 + WM_CHAR = 0x0102 + BM_CLICK = 0x00F5 + WM_COMMAND = 0x0111 + WM_SETTEXT = 0x000C + WM_GETTEXT = 0x000D + + HWND_TOP = 0 + HWND_BOTTOM = 1 + HWND_TOPMOST = -1 + HWND_NOTOPMOST = -2 + + SWP_SHOWWINDOW = 0x40 + SWP_NOSIZE = 1 + SWP_NOMOVE = 2 + + TRUE_1 = 1 + + # these are constants for commonly used windows windows + WINCLASS_DIALOG = "32770" + + # these are the most used methods + + def initialize + @User32 = DL.dlopen("user32") + # we must determine the path we are in + @path_to_clicker = '"' + File.expand_path(File.dirname(__FILE__)) + '"' + end + + + # The system function passes command to the command interpreter, which executes the string as an operating-system command + # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/html/_crt_system.2c_._wsystem.asp + # using win32api + def winsystem(command) + pid = Win32API.new("crtdll", "system", ['P'], 'L').Call(command) + end + + # returns the short path version of a long path + # 8.3 style + def getShortFileName(longName) + size = 255 + buffer = " " * 255 + returnSize = Win32API.new("kernel32" , "GetShortPathNameA" , 'ppl' , 'L').Call(longName , buffer , size ) + a = "" + a = a + buffer[0...returnSize] + return a + end + + # Set the first edit box in the Choose file dialog to textToSet + # we may need to play with the default try count. 3 is a reasonably safe value. + def setFileRequesterFileName( textToSet, tryCount = 3 ) + for i in (1..tryCount) + # first set the Choose File Window to be active + hWnd = getWindowHandle("Choose file" ) + if hWnd != -1 + makeWindowActive(hWnd) + setTextValueForFileNameField( hWnd , textToSet) + clickWindowsButton_hwnd(hWnd, "&Open") + return true + end + end + return false + end + + # fire off setting the file name for the Choose file dialog + # in a new process + def setFileRequesterFileName_newProcess ( textToSet ) + myapp = "rubyw #{@path_to_clicker}/setFileDialog.rb #{textToSet}" + # first argument to system call is a window title, in this case blank "" + winsystem( "start \"\" #{myapp}" ) + end + + # Return the text value from the first combo box + # on the Choose file dialog or nil if not found + def getFileRequesterFileName() + # first set the Choose File Window to be active + hWnd = getWindowHandle("Choose file" ) + if hWnd != -1 + makeWindowActive(hWnd) + return getTextValueForFileNameField( hWnd ) + else + return nil + end + end + + # Click on a dialog with title of "Internet Explorer" + # Default button to click is "OK" + # parenthWnd not used + def clickJavaScriptDialog(button="OK" , parenthWnd = -1) + clickWindowsButton("Internet Explorer" , button ) + end + + # Calls system to launch a new process to click on the button + # defaults to "OK" button + def clickJSDialog_NewProcess(button = "OK" ) + myapp = "rubyw #{@path_to_clicker}/clickJSDialog.rb #{button}" + log "Starting win clicker in a new process. Looking for button #{button}" + log "Starting app: #{myapp}" + # first argument to system call is a window title, in this case blank "" + winsystem( "start \"\" #{myapp}" ) + end + + + # as a thread + def clickJSDialog_Thread(button = "OK" ) + sleep 3 + n = 0 + while n < 3 + sleep 1 + clickWindowsButton("Internet Explorer" , button ) + n=n+1 + end + end + + # Looks for a window titled "Security Alert", clicks + # on Yes button + def clearSecurityAlertBox + clickWindowsButton("Security Alert" , "&Yes" ) + end + alias :clear_security_alert :clearSecurityAlertBox + + # Returns the parent handle for the given child handle + def getParent (childhWnd ) + # pass a hWnd into this function and it will return the parent hWnd + getParentWindow = @User32['GetParent' , 'II' ] + a , b = getParentWindow.call(childhWnd ) + return a + end + alias :get_parent :getParent + + def with_dl_callback(type, prc) + callback = DL.callback(type, &prc) + error = nil + begin + yield callback + ensure + DL.remove_callback(callback) + end + end + + # Enumerates open windows and + # returns a window handle from a given title and window class + # Window class and title are matched regexes + def getWindowHandle(title, winclass = "" ) + enum_windows = @User32['EnumWindows', 'IPL'] + get_class_name = @User32['GetClassName', 'ILpI'] + get_caption_length = @User32['GetWindowTextLengthA' ,'LI' ] # format here - return value type (Long) followed by parameter types - int in this case - see http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/~checkout~/ruby/ext/dl/doc/dl.txt? + get_caption = @User32['GetWindowTextA', 'iLsL' ] + + len = 32 + buff = " " * len + classMatch = false + + bContinueEnum = -1 # Windows "true" to continue enum_windows. + found_hwnd = -1 + + enum_windows_proc = lambda {|hwnd,lparam| + sleep 0.05 + r,rs = get_class_name.call(hwnd, buff, buff.size) + + if winclass != "" then + if /#{winclass}/ =~ rs[1].to_s + classMatch = true + end + else + classMatch = true + end + + if classMatch ==true + textLength, a = get_caption_length.call(hwnd) + captionBuffer = " " * (textLength+1) + t , textCaption = get_caption.call(hwnd, captionBuffer , textLength+1) + if /#{title}/ =~ textCaption[1].to_s + found_hwnd = hwnd + bContinueEnum = 0 # False, discontinue enum_windows + end + bContinueEnum + else + bContinueEnum + end + } + with_dl_callback('ILL',enum_windows_proc) do |callback| + r,rs = enum_windows.call(callback, 0) + end + return found_hwnd + end + alias :get_window_handle :getWindowHandle + + # Call SwitchToThisWindow win32api which will + # The SwitchToThisWindow function is called to switch focus to a specified window + # and bring it to the foreground + def makeWindowActive (hWnd) + switch_to_window = @User32['SwitchToThisWindow' , 'pLI' ] + # set it to be the one with focus + switch_to_window.call(hWnd , 1) + end + alias :make_window_active :makeWindowActive + + # Posts a message to the handle passed in to click + def clickButtonWithHandle(buttonhWnd) + post_message = @User32['PostMessage', 'ILILL'] + r,rs = post_message.call(buttonhWnd, BM_CLICK, 0, 0) + end + alias :click_button_with_handle :clickButtonWithHandle + + # Based on the parent window handle passed in, + # click on the button with the given caption. + def clickWindowsButton_hwnd (hwnd , buttonCaption ) + makeWindowActive(hwnd) + d = getChildHandle( hwnd , buttonCaption ) + if d != -1 + makeWindowActive(hwnd) + clickButtonWithHandle(d) + else + return false + end + return true + end + alias :click_windows_button_hwnd :clickWindowsButton_hwnd + + # this clicks the button with the name in the window with the caption. It keeps looking for the button until + # until the timeout expires + def clickWindowsButton (windowCaption , buttonCaption , maxWaitTime=30 ) + sleep 1 + hwnd = -1 + begin + timeout(maxWaitTime) do + hwnd = getWindowHandle(windowCaption) + while hwnd == -1 + hwnd = getWindowHandle(windowCaption) + sleep 0.5 + end + makeWindowActive(hwnd) + end + rescue Timeout::Error + return false + rescue => e + raise e + end + if hwnd != -1 + makeWindowActive(hwnd) + else + end + d = getChildHandle( hwnd , buttonCaption ) + if d != -1 + makeWindowActive(hwnd) + clickButtonWithHandle(d) + else + return false + end + return true + end + alias :click_windows_button :clickWindowsButton + + # Enumerate through children of the parent hwnd, pass back + # the handle for the control with the given caption + # the caption is compared as a regex + def getChildHandle ( hWnd , childCaption ) + enum_childWindows = @User32['EnumChildWindows' , 'IIPL' ] + get_caption_length = @User32['GetWindowTextLengthA' ,'LI' ] # format here - return value type (Long) followed by parameter types - int in this case - see http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/~checkout~/ruby/ext/dl/doc/dl.txt? + get_caption = @User32['GetWindowTextA', 'iLsL' ] + match_hwnd = -1 # hWnd of handle matching childCaption + buff = " " * 16 + get_class_name = @User32['GetClassName', 'ILpI'] + + bContinueEnum = -1 + enum_childWindowsProc = lambda {|chwnd,lparam| + r,rs = get_class_name.call(chwnd, buff, buff.size) + textLength, a = get_caption_length.call(chwnd) + captionBuffer = " " * (textLength+1) + + t , textCaption = get_caption.call(chwnd, captionBuffer , textLength+1) + if /#{childCaption}/ =~ textCaption[1].to_s then + match_hwnd = chwnd + bContinueEnum = 0 # Windows "false" to discontinue enum_childWindow + end + bContinueEnum + } + with_dl_callback('ILL',enum_childWindowsProc) do |callback| + r = enum_childWindows.call(hWnd, callback ,0) + end + return match_hwnd + end + alias :get_chwnd :getChildHandle + + # Convenience method to return Static text for + # children of the window with the given caption + def getStaticText(caption) + return getStaticTextFromWindow(caption, -1) + end + alias :get_static_text :getStaticText + + # Convenience method to return Static text for + # children of the window handle + def getStaticText_hWnd (hWnd) + return getStaticTextFromWindow("" , hWnd) + end + alias :get_static_text_hwnd :getStaticText_hWnd + + # Return text as an array from child controls of the window + # given as either a handle or with the given caption + # that have a class type of Static + def getStaticTextFromWindow( windowCaption , hWnd) + enum_childWindows = @User32['EnumChildWindows' , 'IIPL' ] + get_caption_length = @User32['GetWindowTextLengthA' ,'LI' ] # format here - return value type (Long) followed by parameter types - int in this case - see http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/~checkout~/ruby/ext/dl/doc/dl.txt? + get_caption = @User32['GetWindowTextA', 'iLsL' ] + + staticText = [] + buff = " " * 16 + get_class_name = @User32['GetClassName', 'ILpI'] + + if hWnd == -1 + hWnd = getWindowHandle(windowCaption) + end + + if hWnd == -1 + return staticText + end + + bContinueEnum = -1 + enum_childWindows_proc = lambda {|hWnd,lparam| + r,rs = get_class_name.call(hWnd, buff, buff.size) + if rs[1].to_s == "Static" # there must be a better way of detecting this + textLength, a = get_caption_length.call(hWnd) + captionBuffer = " " * (textLength+1) + t , textCaption = get_caption.call(hWnd, captionBuffer , textLength+1) + staticText << textCaption[1].to_s + end + bContinueEnum + } + with_dl_callback('ILL',enum_childWindows_proc) do |callback| + r = enum_childWindows.call(hWnd, callback ,0) + end + return staticText + end + alias :get_static_text_from_window :getStaticTextFromWindow + + # returns the handle (or -1 if its not found) of the + # nth control of this class in the parent window specified + # by the window handle + def getHandleOfControl (hWnd , controlClass, position ) + enum_childWindows = @User32['EnumChildWindows' , 'IIPL' ] + get_caption_length = @User32['GetWindowTextLengthA' ,'LI' ] # format here - return value type (Long) followed by parameter types - int in this case - see http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/~checkout~/ruby/ext/dl/doc/dl.txt? + get_caption = @User32['GetWindowTextA', 'iLsL' ] + control_hWnd = [] + buff = " " * 16 + get_class_name = @User32['GetClassName', 'ILpI'] + + bContinueEnum = -1 + enum_childWindows_proc = lambda {|hWnd,lparam| + r,rs = get_class_name.call(hWnd, buff, buff.size) + if rs[1].to_s == controlClass # there must be a better way of detecting this + control_hWnd << hWnd + end + bContinueEnum + } + with_dl_callback('ILL',enum_childWindows_proc) do |callback| + r = enum_childWindows.call(hWnd, callback ,0) + end + controlHwnd = control_hWnd[position] + if controlHwnd == nil then + controlHwnd = -1 + end + return controlHwnd + end + alias :get_handle_of_ctrl :getHandleOfControl + + # Call set text on the given window handle + def setComboBoxText(hWnd , textToSet) + set_text(hWnd, textToSet) + end + alias :set_combo_txt :setComboBoxText + + # Call set text on the given window handle + def setTextBoxText(hWnd , textToSet) + set_text(hWnd, textToSet) + end + alias :set_textbox_txt :setTextBoxText + + # Private method to set text called by the two methods above + def set_text(hWnd, textToSet) + send_message = @User32['SendMessage', 'ILISS'] + r ,rs = send_message.call(hWnd , WM_SETTEXT ,'',textToSet) + end + private :set_text + + # Get the text in the handle for the given control + def getControlText(hWnd) + buff = " " * 256 + send_message = @User32['SendMessage', 'ILIIS'] + r ,rs = send_message.call(hWnd , WM_GETTEXT , 256 , buff ) + return buff.to_s + end + alias :get_ctrl_txt :getControlText + + # get the title for the specified hwnd + def getWindowTitle(hWnd) + buff = " " * 256 + getWindowText = @User32['GetWindowText' , 'ILSI'] + r , rs = getWindowText.call( hWnd , buff , 256 ) + return buff.to_s + end + alias :get_win_title :getWindowTitle + + # Get the text in the first combo box + # file requester methods returns nil on failure to + # locate the 1st combobox + def getTextValueForFileNameField(parenthWnd) + f = getHandleOfControl(parenthWnd, "ComboBox", 1) + if f == -1 then + # unable to find the first combobox + return nil + else + # we have the control and now + # can send it some messages + return getWinText(f ) + end + end + alias :get_file_name :getTextValueForFileNameField + + # this sets the filename field to text to set + def setTextValueForFileNameField( parenthWnd , textToSet ) + # get the handle of the nth control that is an Edit box + f = getHandleOfControl(parenthWnd, "Edit" , 0 ) + if f == -1 then + # unable to get a handle on the first edit control + return false + else + # we found the control and can now send it some messages + setComboBoxText(f , textToSet) + return true + end + end + alias :set_file_name :setTextValueForFileNameField +end