junkew

IUIAutomation MS framework automate chrome, FF, IE, ....

562 posts in this topic

@Junkew -

This may be obvious but any help the spy can assist with in identifying the control automatically to the point where it takes some of the complexity away from interpreting the info returned would be huge.  Example, if I Spy the Word app can the Spy figure out the PID and call _ProcessGetName($iPID) to do something like this?:

_UIA_setVar("[RETURNED PROCESS NAME].mainwindow","classname:=[RETURNED CLASS NAME]")

In this example the tool spied everything but might ask me to name the control (i.e. "mainwindow").  I believe the PID would return "Word.exe."  I realize this will not always work because there can be more than one control with the same name but can the Spy search for the instance that responds to the coordinates of the control selected in the Spy and determine which instance it is?  I am just tossing stuff out to see how automated the identification can be.  In an ideal world I highlight a control and the Spy writes the code to call it with little or no input.


Build your own poker game with AutoIt: pokerlogic.au3 | Learn To Program Using FREE Tools with AutoIt

Share this post


Link to post
Share on other sites



I think in my post (# 57 = ) that i was looking for the RTI object in the wrong spot.

junkew, does this make more sense?

func _UIA_action($obj_or_string, $strAction, $p1=0, $p2=0, $p3=0)

    local $tPattern
    local $x, $y
;~  local $objElement
    local $oElement

;~ If we are giving a description then try to make an object first by looking from repository
;~ Otherwise assume an advanced description we should search under one of the previously referenced elements at runtime
    if isobj($obj_or_string) or IsObj(_UIA_getVar("RTI." & $obj_or_string)) Then ; check if obj_or_string is an object, or if it is stored in the RTI
        If isobj($obj_or_string) Then ; if object, use it
            $oElement = $obj_or_string
            $obj = $obj_or_string
        Else ; if RTI.object, use that
            $oElement = _UIA_getVar("RTI." & $obj_or_string)
            $obj = _UIA_getVar("RTI." & $obj_or_string)
        EndIf
    else
; rest of function....

Share this post


Link to post
Share on other sites

#63 ·  Posted (edited)

@JFish: I will extend the simple spy with parent information and with a simple editbox where you directly can test the given description by highlighting it once you press a button highlight. Will do that somewhere coming days. A more advanced spy is on the TODO list but not on short notice
 
@Shrapnel:
Technically all objects are in UI Automation tree and within this abstraction framework I make a Run Time Information tree / dictionary for some frequently used objects like the mainwindow. I am a little in doubt if I should add all objects that are used in the RTI tree. Initially I started by only keeping a quick reference to the MAINWINDOW as its a frequent starting point for searching. If I would put all objects in RTI tree I am actually rebuilding the caching of UIA framework itself and thats for sure not what my goal is.

  • So to answer your question:

I try to abstract this piece of code but not fully cleared up my mind what should be in RTI and what not. I can also imagine that I want to have much more information in the RTI tree for heuristically finding an object back when the main properties given cannot identify the control.

 
Some directions I want to go 
syntax like this

_UIA_Action("IE.MainWindow","menu","File,Send,Page by Email","click")

 So meaning look under mainwindow for a menu and click sequentially the menu/submenu's on the text as given
But when you need it multilanguage (english and dutch for example) I would like to have defined as a replacing reference logic without touching the _UIA_action call only by adding the descriptions to the RTI (so maintenance primarily is done by maintaining the UI definition map)

File:=((File)|(Bestand))
Send:=((Send)|(Verstuur))
Page by Email:=((Page by Email)|(Pagina per email))

 and even worse the menu titles can be different per browser (IE, Chrome, Firefox, Safari) where I prefer to have the main scripts equal and deal with the details only by giving different definitions or use more complicated regular expressions

 
 Working currently on

  • UID files to create a User Interface Definition file which describes the logical and physical pattern
  • UIA.CFG file to have the basic settings configurable thru a file (and if not there use some defaults)
  • CFG files per user (for multiuser environment)
  • extending some actions in the _UIA_Action

 

Edited by junkew

Share this post


Link to post
Share on other sites

#64 ·  Posted (edited)

Not sure if this would be possible (or worth it), but i think it would be nice if multiple attributes could be defined to a control at one time similar to how controls are handled with Autoit http://www.autoitscript.com/autoit3/docs/intro/controls.htm

 

meaning _UIA_setVar() would be set like this:

_UIA_setVar("OrderTracking.btnNewOrder", "[automationid:=btnNewOrder; class:=WindowsForms10.BUTTON.app.0.267e1d0_r13_ad1]")

and when a function like _UIA_Action is called, both of those attributes would have to match

 

 

Edit: Sorry, i didn't even notice that this was already on the TODO list!

TODO

  • Build recorder
  • Enhance the spy with a nicer UI
  • UI for the repository (now in the script with dot notation)
  • Enhance mapping / identifying on multiple properties instead of 1 combined with index
  • If speed becomes an issue use the caching logic of the MS UIA framework
Edited by Shrapnel

Share this post


Link to post
Share on other sites

#65 ·  Posted (edited)

Fantastic work !

Just one question:

how to get an object from a native window handle without searching throuh all desktop children?

lets say for an explorer listview

hListview=ControlGetHandle($hWin,'','[CLASS:DirectUIHWND]')

I tried:

 $objUIAutomation.ElementFromHandle( $hListview, $pUIElement )
 $oUIElement = objcreateinterface( $pUIElement , $sIID_IUIAutomationElement , $dtagIUIAutomationElement )

but it crashes.

And in Snippet from "trancexxx" I dont know what to place for  "sIID_IAccessible"

Func objFromHandle( $hWindow )

    $tIID_IAccessible = _WinAPI_GUIDFromString( $sIID_IAccessible )

    $aCall  = DllCall($hOLEACC_DLL, "long", "AccessibleObjectFromWindow", "hwnd", $hWindow, "dword", $OBJID_CLIENT, "struct*", $tIID_IAccessible, "ptr*", Null)
    $pIAccessible =  $aCall[4]

    ; Object from pointer
     $oIAccessible = ObjCreateInterface( $pIAccessible , $sIID_IUIAutomationElement, $dtagIUIAutomationElement )

EndFunc

???

Thanks for suggestions

Bluesmaster

Edited by Bluesmaster

My UDF: [topic='156155']_shellExecuteHidden[/topic]

Share this post


Link to post
Share on other sites

@Bluesmaster: 
First part I can answer second part probably TranceXXX can explain that in a better way

 $objUIAutomation.ElementFromHandle( $hListview, $pUIElement )  

 
You have to fix in CUIAutomation2. There seems to be some none definitions in parameters that should be  
 

"ElementFromHandle hresult(hwnd;ptr*);" & _

@Shrapnel: 

  • Yes multiple properties matching is definitely on my TODO list on relatively short term to fix that 

Share this post


Link to post
Share on other sites

Updated first post with new wrappers/fixes etc.
 
For this example to work you have to make a file UIA.CFG where you can store some of the data

For identifying controls I try to put in the dutch and english values for propery names. Sometimes its a best guess for english (I test against a dutch windows)
 
Example 11 Word 2010, Calculator, Notepad

#include "UIAWrappers.au3"

#AutoIt3Wrapper_UseX64=N

;~ Start the system under test applications
_UIA_StartSUT("SUT1") ;~Calculator
_UIA_StartSUT("SUT2") ;~Notepad
_UIA_StartSUT("SUT3") ;~MS Word

;~ To be moved to UID (User interface definition) files
;~ Set the system under test UID objects to recognize
local $UID_WORD[4][2] = [ _
["mainwindow","classname:=OpusApp"], _
["btnZoeken","name:=((Zoeken.*)|(Find.*)); ControlType:=Button; acceleratorkey:=Ctrl\+F"] , _
["document","classname:=_WwG"], _
["btnBold","name:=((Vet)|(Bold))"] _
]
_UIA_setVarsFromArray($UID_Word,"Word.")

local $UID_CALC[7][2] = [ _
["mainwindow","classname:=CalcFrame"], _
["1","name:=1; controltype:=button"],  _
["2","name:=2; controltype:=button"] , _
["3","name:=3; controltype:=button"] , _
["BACKSPACE","AutomationId:=83"] , _
["mnuEdit","name:=((Edit)|(Bewerken)); controltype:=MenuItem"], _
["mnuCopy","name:=((Copy.*)|(Kopi.*)); controltype:=MenuItem"] _
]
_UIA_setVarsFromArray($UID_CALC,"Calculator.")

local $UID_NOTEPAD[4][2] = [ _
["mainwindow","classname:=Notepad"], _
["title","controltype:=50037"], _
["mnuEdit","name:=((Edit)|(Bewerken))"], _
["mnuPaste","name:=((Paste.*)|(Plak.*))"] _
]
_UIA_setVarsFromArray($UID_NOTEPAD,"Notepad.")

;~ To be moved to 1 or multiple scriptfiles
;- Set the script
;~ The actual script, 50,10 is only there if a lot of lines and parameters are needed

local $script[50][10]= [ _
["Word.Mainwindow","setfocus"], _
["Word.btnBold",   "click"], _
["word.document",  "sendkeys", "hello world"], _
["word.btnZoeken", "click"],  _
["calculator.mainwindow", "setfocus"], _
["calculator.1","click"], _
["calculator.2","click"], _
["calculator.3","click"], _
["calculator.backspace","click"], _
["calculator.mnuEdit","click"], _
["calculator.mnuCopy","click"], _
["notepad.mainwindow", "setfocus"], _
["notepad.mnuEdit","click"], _
["notepad.mnuPaste","click"], _
["notepad.mainwindow","setvalue", "hello world"] _
]

_UIA_launchScript($script)

Exit

save this to UIA.CFG file

; This is an inifile for UIA wrappers having the configuration defaults
; Debug=true        Turn debugging on of off with true/false value
; Highlight=true    Turn Highlighting rectangle to true / false 
; TODO: AutoStartSUT=true AutoStartSUT is starting all SUT's automatically

[Global]
Debug=true
Highlight=true
AutoStartSUT=true

[Multiuser]
CFGPerUser=false

[Folders]
subfolders=false

;System under test settings
; Folder      = Location where exe can be found
; Workingdir  = Location of the working directory
; exe         = Name of the exe to start
; Fullname    = Path & name of exe 
; Windowstate = minimized, maximized, normal

[SUT1]
Folder=%Windowsdir%\system32
Workingdir=%Windowsdir%\system32
exe=calc.exe
Fullname=%Windowsdir%\system32\calc.exe
Parameters=
Processname=calc.exe
Windowstate=normal

[SUT2]
Folder=%Windowsdir%\system32
Workingdir=%Windowsdir%\system32
exe=notepad.exe
Fullname=%Windowsdir%\system32\notepad.exe
Parameters=
Processname=notepad.exe
Windowstate=normal  

[SUT3]
C:\Program Files (x86)
Folder=%programfilesdir%\Microsoft Office\Office14
Workingdir=%programfilesdir%\Microsoft Office\Office14
exe=winword.exe
Fullname=%programfilesdir%\Microsoft Office\Office14\winword.exe
Parameters=
Processname=winword.exe
Windowstate=normal
1 person likes this

Share this post


Link to post
Share on other sites

This is AMAZING! Please keep working on this! someone needs to UDF this. It's amazing! I especially like the chrome automation, no plugins required! Being able to do IE and Excel and the other stuff is just awesome. I am nowhere near this level of programming, but i hope this makes it to a standard UDF

Good luck, and thankyou


0x616e2069646561206973206c696b652061206d616e20776974686f7574206120626f64792c20746f206669676874206f6e6520697320746f206e657665722077696e2e2e2e2e

Share this post


Link to post
Share on other sites

 Can any one give me a solutions for the below mentioned question

1. Solutions to "wait for page load" on chrome. Similar to _IELoadWait

2. Click on element by ID

These options are much much needed for me.. can any one Solve this.

Share this post


Link to post
Share on other sites

#71 ·  Posted (edited)

I needed information about the controls in a Windows Explorer window for a project (Windows Explorer right pane) I'm working on. It was easy to get the information with this CUIAutomation2.au3 UDF.

Here is the code I used. It's very simple. The elements of the Automation tree are extracted with a recursive function. The parent-child relationships of the controls are printed in Scite console in a hierarchical structure like a treeview. This gives a nice overview.

#include <WinAPI.au3>
#include "CUIAutomation2.au3"

Opt( "MustDeclareVars", 1 )

Global $oUIAutomation

Example()


; List all automation elements of a window
; in a hierarchical structure like a treeview.
Func Example()
  ; Get window handle
  ;Local $hWindow = WinGetHandle( "[CLASS:WindowsForms10.Window.8.app.0.bb8560_r19_ad1]" ) ; Windows Forms
  ;Local $hWindow = WinGetHandle( "[REGEXPCLASS:WindowsForms10.Window.8.app.*]" )          ; Windows Updates Downloader
  ;Local $hWindow = WinGetHandle( "BCGPVisualStudioGUIDemo - Start Page" )                 ; BCGSoft BCGPVisualStudioGUIDemo
  ;Local $hWindow = WinGetHandle( "[REGEXPCLASS:^(Cabinet|Explore)WClass$]" )              ; Windows Explorer on XP, Vista, 7, 8, 10
  ;Local $hWindow = WinGetHandle( "[TITLE:My GUI Checkbox; CLASS:AutoIt v3 GUI]" )         ; AutoIt GUI window
  ;Local $hWindow = WinGetHandle( "[CLASS:AutoIt v3 GUI]" )                                ; AutoIt script
  ;Local $hWindow = WinGetHandle( "[CLASS:Chrome_WidgetWin_1]" )                           ; Chrome
  ;Local $hWindow = WinGetHandle( "[CLASS:IEFrame]" )                                      ; Internet Explorer
  ;Local $hWindow = WinGetHandle( "Calculator" )                                           ; Calculator
  Local $hWindow = _WinAPI_GetDesktopWindow()                                              ; Desktop. Beware. Can print a lot of information.
  If Not $hWindow Then Return ConsoleWrite( "Window handle error" & @CRLF )

  ; Create UI Automation object
  $oUIAutomation = ObjCreateInterface( $sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation )
  If Not IsObj( $oUIAutomation ) Then Return ConsoleWrite( "UI Automation object error" & @CRLF )

  ; Get UI Automation element from window handle
  Local $pWindow, $oWindow
  $oUIAutomation.ElementFromHandle( $hWindow, $pWindow )
  $oWindow = ObjCreateInterface( $pWindow, $sIID_IUIAutomationElement, $dtagIUIAutomationElement )
  If Not IsObj( $oWindow ) Then Return ConsoleWrite( "Automation element from window error" & @CRLF )

  ; List all elements of window
  ListDescendants( $oWindow, 0, 0 )
EndFunc

; List all child elements of parent
Func ListDescendants( $oParent, $iLevel, $iLevels = 0 )
  If Not IsObj( $oParent ) Then Return
  If $iLevels And $iLevel = $iLevels Then Return

  ; Create RawViewWalker object
  Local $pRawViewWalker, $oRawViewWalker
  $oUIAutomation.RawViewWalker( $pRawViewWalker )
  $oRawViewWalker = ObjCreateInterface( $pRawViewWalker, $sIID_IUIAutomationTreeWalker, $dtagIUIAutomationTreeWalker )
  If Not IsObj( $oRawViewWalker ) Then Return ConsoleWrite( "RawViewWalker object error" & @CRLF )

  ; Get first child element
  Local $pUIElement, $oUIElement
  $oRawViewWalker.GetFirstChildElement( $oParent, $pUIElement )
  $oUIElement = ObjCreateInterface( $pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement )

  Local $sIndent = ""
  For $i = 0 To $iLevel - 1
    $sIndent &= "    "
  Next

  While IsObj( $oUIElement )
    ConsoleWrite( $sIndent & "Title     = " & GetCurrentPropertyValue( $oUIElement, $UIA_NamePropertyId ) & @CRLF & _
                  $sIndent & "Class     = " & GetCurrentPropertyValue( $oUIElement, $UIA_ClassNamePropertyId ) & @CRLF & _
                  $sIndent & "Ctrl type = " & GetCurrentPropertyValue( $oUIElement, $UIA_ControlTypePropertyId ) & @CRLF & _
                  $sIndent & "Ctrl name = " & GetCurrentPropertyValue( $oUIElement, $UIA_LocalizedControlTypePropertyId ) & @CRLF & _
                  $sIndent & "Value     = " & GetCurrentPropertyValue( $oUIElement, $UIA_LegacyIAccessibleValuePropertyId ) & @CRLF & _
                  $sIndent & "Handle    = " & Hex( GetCurrentPropertyValue( $oUIElement, $UIA_NativeWindowHandlePropertyId ) ) & @CRLF & @CRLF )

    ListDescendants( $oUIElement, $iLevel + 1, $iLevels )

    $oRawViewWalker.GetNextSiblingElement( $oUIElement, $pUIElement )
    $oUIElement = ObjCreateInterface( $pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement )
  WEnd
EndFunc

Func GetCurrentPropertyValue( $oObject, $iPropertyId )
  Local $vValue, $sString
  $oObject.GetCurrentPropertyValue( $iPropertyId, $vValue )
  If Not IsArray( $vValue ) Then Return $vValue
  $sString = $vValue[0]
  For $i = 1 To UBound( $vValue ) - 1
    $sString &= "; " & $vValue[$i]
  Next
  Return $sString
EndFunc

Tested on Windows 7 64 bit and Windows XP 32 bit. If you are on a 64 bit Windows you must run the example as a 64 bit program.

Note that this example does not handle virtual items in Windows Explorer listview. To handle virtual items see post 94 (example 18 in the list in first post).

Note also that this example is not limited to Windows Explorer. It can print the Automation tree (hierarchical structure of all elements) for any window.

Edited by LarsJ
Code updated

Share this post


Link to post
Share on other sites

And of course you can still get some information with the functions in WinAPI.au3:

#include <Constants.au3>
#include <WinAPI.au3>

Opt( "MustDeclareVars", 1 )

MainFunc()


Func MainFunc()

  ;Local $hWindow = _WinAPI_GetDesktopWindow()                        ; Desktop
  Local $hWindow = WinGetHandle( "[CLASS:CabinetWClass]", "" )        ; Windows Explorer, Windows 7
  ;Local $hWindow = WinGetHandle( "[CLASS:ExploreWClass]", "" )       ; Windows Explorer, Windows XP
  ;Local $hWindow = WinGetHandle( "Windows Explorer right pane", "" ) ; Windows Explorer right pane
  ;Local $hWindow = WinGetHandle( "[CLASS:IEFrame]", "" )             ; Internet Explorer
  ;Local $hWindow = WinGetHandle( "Calculator" )                      ; Calculator
  If Not $hWindow Then Return

  ;ListDescendants( $hWindow, 0, 1 ) ; Desktop
  ListDescendants( $hWindow, 0, 0 )  ; Window

EndFunc


Func ListDescendants( $hParent, $iLevel, $iLevels = 0 )

  If Not $hParent Then Return
  If $iLevels And $iLevel = $iLevels Then Return

  Local $hChild = _WinAPI_GetWindow( $hParent, $GW_CHILD )

  Local $sIndent = ""
  For $i = 0 To $iLevel - 1
    $sIndent &= "    "
  Next

  While $hChild
    ConsoleWrite( $sIndent & "Handle = " & Hex( $hChild ) & @CRLF & _
                  $sIndent & "Class  = " & _WinAPI_GetClassName( $hChild ) & @CRLF & _
                  $sIndent & "Title  = " & _WinAPI_GetWindowText( $hChild ) & @CRLF & @CRLF )

    ListDescendants( $hChild, $iLevel + 1, $iLevels )

    $hChild = _WinAPI_GetWindow( $hChild, $GW_HWNDNEXT )
  WEnd

EndFunc

Share this post


Link to post
Share on other sites

#73 ·  Posted (edited)

The examples in post #71 generates a lot of information. For the examples related to Windows Explorer you might be interested only in data for the listview. This example shows how to limit the data to a named control class which is a descendant of the parent window.

On Windows 7 the class name for the Windows Explorer listview is UIItemsView. On Windows XP it's SysListView32. If you are on Vista or Windows 8 you have to figure out the names yourself by running the code in post #71.

Only the function ListClassDescendants (10 lines) is new code. The rest is copied from post #71.

#include "CUIAutomation2.au3"

Opt( "MustDeclareVars", 1 )

Global $oUIAutomation

MainFunc()


Func MainFunc()

  Local $hWindow = WinGetHandle( "[CLASS:CabinetWClass]", "" )        ; Windows Explorer, Windows 7
  ;Local $hWindow = WinGetHandle( "[CLASS:ExploreWClass]", "" )       ; Windows Explorer, Windows XP
  ;Local $hWindow = WinGetHandle( "Windows Explorer right pane", "" ) ; Windows Explorer right pane
  ;Local $hWindow = WinGetHandle( "[CLASS:IEFrame]", "" )             ; Internet Explorer
  ;Local $hWindow = WinGetHandle( "Calculator" )                      ; Calculator
  If Not $hWindow Then Return

  $oUIAutomation = ObjCreateInterface( $sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation )
  If Not IsObj( $oUIAutomation ) Then Return

  Local $pWindow
  $oUIAutomation.ElementFromHandle( $hWindow, $pWindow )
  If Not $pWindow Then Return

  Local $oWindow = ObjCreateInterface( $pWindow, $sIID_IUIAutomationElement, $dtagIUIAutomationElement )
  If Not IsObj( $oWindow ) Then Return

  ListClassDescendants( $oWindow, "UIItemsView" )    ; Windows Explorer, Windows 7
  ;ListClassDescendants( $oWindow, "SysListView32" ) ; Windows Explorer, Windows XP

EndFunc


Func ListClassDescendants( $oWindow, $sClass )

  Local $pCondition
  $oUIAutomation.CreatePropertyCondition( $UIA_ClassNamePropertyId, $sClass, $pCondition )
  If Not $pCondition Then Return

  Local $pUIElement, $oUIElement
  $oWindow.FindFirst( $TreeScope_Descendants, $pCondition, $pUIElement )
  $oUIElement = ObjCreateInterface( $pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement )
  If Not IsObj( $oUIElement ) Then Return

  ListDescendants( $oUIElement, 0, 1 )

EndFunc


; Same function as in post #71
Func ListDescendants( $oParent, $iLevel, $iLevels = 0 )

  If Not IsObj( $oParent ) Then Return
  If $iLevels And $iLevel = $iLevels Then Return

  Local $pRawWalker, $oRawWalker
  $oUIAutomation.RawViewWalker( $pRawWalker )
  $oRawWalker = ObjCreateInterface( $pRawWalker, $sIID_IUIAutomationTreeWalker, $dtagIUIAutomationTreeWalker )

  Local $pUIElement, $oUIElement
  $oRawWalker.GetFirstChildElement( $oParent, $pUIElement )
  $oUIElement = ObjCreateInterface( $pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement )

  Local $sIndent = ""
  For $i = 0 To $iLevel - 1
    $sIndent &= "    "
  Next

  While IsObj( $oUIElement )
    ConsoleWrite( $sIndent & "Title    = " & _UIA_getPropertyValue( $oUIElement, $UIA_NamePropertyId ) & @CRLF & _
                  $sIndent & "Selected = " & _UIA_getPropertyValue( $oUIElement, $UIA_SelectionItemIsSelectedPropertyId ) & @CRLF & _
                  $sIndent & "Class    = " & _UIA_getPropertyValue( $oUIElement, $UIA_ClassNamePropertyId ) & @CRLF & _
                  $sIndent & "Type     = " & _UIA_getPropertyValue( $oUIElement, $UIA_LocalizedControlTypePropertyId ) & @CRLF & _
                  $sIndent & "Handle   = " & Hex( _UIA_getPropertyValue( $oUIElement, $UIA_NativeWindowHandlePropertyId ) ) & @CRLF & @CRLF )

    ListDescendants( $oUIElement, $iLevel + 1, $iLevels )

    $oRawWalker.GetNextSiblingElement( $oUIElement, $pUIElement )
    $oUIElement = ObjCreateInterface( $pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement )
  WEnd

EndFunc


; Same function as in post #71
Func _UIA_getPropertyValue( $obj, $id )
  Local $tVal
  $obj.GetCurrentPropertyValue( $id, $tVal )
  If Not IsArray( $tVal ) Then Return $tVal
  Local $tStr = $tVal[0]
  For $i = 1 To UBound( $tVal ) - 1
    $tStr &= "; " & $tVal[$i]
  Next
  Return $tStr
EndFunc

Here is an example of the output on Windows 7:

Title    = Header
Selected = False
Class    = UIViewHeader
Type     = header
Handle   = 00000000

Title    = Example n1.au3
Selected = True
Class    = UIItem
Type     = list item
Handle   = 00000000

Title    = Example n1-a.au3
Selected = False
Class    = UIItem
Type     = list item
Handle   = 00000000

Title    = zLink
Selected = False
Class    = UIItem
Type     = list item
Handle   = 00000000

Tested on Windows 7 64 bit and Windows XP 32 bit. If you are on a 64 bit Windows you must run the example as a 64 bit program.

Note that this example does not handle virtual items in Windows Explorer listview. To handle virtual items see post 94 (example 18 in the list in first post).

Note also that this example is not limited to Windows Explorer. It can print the descendants of an arbitrary named control class.

Last edit: Notes.

Edited by LarsJ

Share this post


Link to post
Share on other sites

Has someone an example for controlling Silverlight object ?

Thanks in advance 

Share this post


Link to post
Share on other sites

use the simple spy and hover over the silverlight object then you see the details to recognize on.

If you do not understand post the information the spy gives and I can give an example

Share this post


Link to post
Share on other sites

junkew, I'm using the recursive procedure to print the parent-child relationships of the controls in a hierarchical structure like a treeview. This gives a nice overview. That's why I'm not using FindAll.

Share this post


Link to post
Share on other sites

#78 ·  Posted (edited)

This example prints the items of the Windows Explorer listview in Scite console. The selected state of the items is printed too.

#include "..\Include\CUIAutomation2.au3"

Opt( "MustDeclareVars", 1 )

Global $oUIAutomation

MainFunc()


Func MainFunc()

  ; Be sure to use the right class if you are on Vista or Windows 8
  Local $hWindow = WinGetHandle( "[CLASS:CabinetWClass]", "" )  ; Windows Explorer, Windows 7
  ;Local $hWindow = WinGetHandle( "[CLASS:ExploreWClass]", "" ) ; Windows Explorer, Windows XP
  If Not $hWindow Then Return

  $oUIAutomation = ObjCreateInterface( $sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation )
  If Not IsObj( $oUIAutomation ) Then Return

  Local $pWindow
  $oUIAutomation.ElementFromHandle( $hWindow, $pWindow )
  If Not $pWindow Then Return

  Local $oWindow = ObjCreateInterface( $pWindow, $sIID_IUIAutomationElement, $dtagIUIAutomationElement )
  If Not IsObj( $oWindow ) Then Return

  ListAllItems( $oWindow, 50007 ) ; 50007 = List view element
  ; Run the code in post #71 to get this value

EndFunc


Func ListAllItems( $oWindow, $iCtrlType )

  Local $pCondition
  $oUIAutomation.CreatePropertyCondition( $UIA_ControlTypePropertyId, $iCtrlType, $pCondition )
  If Not $pCondition Then Return

  Local $pUIElementArray, $oUIElementArray, $iElements
  $oWindow.FindAll( $TreeScope_Descendants, $pCondition, $pUIElementArray )
  $oUIElementArray = ObjCreateInterface( $pUIElementArray, $sIID_IUIAutomationElementArray, $dtagIUIAutomationElementArray )
  $oUIElementArray.Length( $iElements )
  If Not $iElements Then Return

  Local $pUIElement, $oUIElement, $name, $sel
  For $i = 0 To $iElements - 1
    $oUIElementArray.GetElement( $i, $pUIElement )
    $oUIElement = ObjCreateInterface( $pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement )

    $oUIElement.GetCurrentPropertyValue( $UIA_NamePropertyId, $name )
    $oUIElement.GetCurrentPropertyValue( $UIA_SelectionItemIsSelectedPropertyId, $sel )
    ConsoleWrite( $name & "  " & $sel & @CRLF )
  Next

EndFunc

Note that this example does not handle virtual items. To handle virtual items see post 94 (example 18 in the list in first post).

Last edit: Note.

Edited by LarsJ

Share this post


Link to post
Share on other sites

The information I needed in post #71 was a chain of controls from Windows Explorer top window to the listview. The code in post #71 prints a lot of information. To get this chain for more folders it would be advantageous to be able to print the chain directly. This can be done if you start to find the listview and move upwards through the parent controls to the top window:

 

#include "..\Include\CUIAutomation2.au3"

Opt( "MustDeclareVars", 1 )

Global $oUIAutomation

MainFunc()


Func MainFunc()

  ; Be sure to use the right class if you are on Vista or Windows 8
  Local $hWindow = WinGetHandle( "[CLASS:CabinetWClass]", "" )  ; Windows Explorer, Windows 7
  ;Local $hWindow = WinGetHandle( "[CLASS:ExploreWClass]", "" ) ; Windows Explorer, Windows XP
  If Not $hWindow Then Return

  $oUIAutomation = ObjCreateInterface( $sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation )
  If Not IsObj( $oUIAutomation ) Then Return

  Local $pWindow
  $oUIAutomation.ElementFromHandle( $hWindow, $pWindow )
  If Not $pWindow Then Return

  ListParents( $pWindow, 50008 ) ; 50008 = SysListView32 on XP ; 50008 = UIItemsView on 7
  ; Run the code in post #71 to get this value

EndFunc


Func ListParents( $pWindow, $iCtrlType )

  Local $oWindow = ObjCreateInterface( $pWindow, $sIID_IUIAutomationElement, $dtagIUIAutomationElement )
  If Not IsObj( $oWindow ) Then Return

  Local $pCondition
  $oUIAutomation.CreatePropertyCondition( $UIA_ControlTypePropertyId, $iCtrlType, $pCondition )
  If Not $pCondition Then Return

  Local $pUIParent, $aUIParents[20], $iUIParents = 0
  $oWindow.FindFirst( $TreeScope_Descendants, $pCondition, $pUIParent )
  If Not $pUIParent Then Return

  $aUIParents[$iUIParents] = $pUIParent
  $iUIParents += 1

  Local $pRawWalker, $oRawWalker, $same
  $oUIAutomation.RawViewWalker( $pRawWalker )
  $oRawWalker = ObjCreateInterface( $pRawWalker, $sIID_IUIAutomationTreeWalker, $dtagIUIAutomationTreeWalker )
  If Not IsObj( $oRawWalker ) Then Return

  While Not $same
    $oRawWalker.GetParentElement( $pUIParent, $pUIParent )
    $oUIAutomation.CompareElements( $pWindow, $pUIParent, $same )
    $aUIParents[$iUIParents] = $pUIParent
    $iUIParents += 1
  WEnd

  Local $oUIParent, $sClass, $sIndent = ""
  For $i = $iUIParents - 1 To 0 Step -1
    $oUIParent = ObjCreateInterface( $aUIParents[$i], $sIID_IUIAutomationElement, $dtagIUIAutomationElement )
    $oUIParent.GetCurrentPropertyValue( $UIA_ClassNamePropertyId, $sClass )
    ConsoleWrite( $sIndent & $sClass & @CRLF )
    $sIndent &= "    "
  Next

EndFunc

That was my first test of this UDF. Not the last. It seems to be very good.

Documentation:

UI Automation Client Programmer's Guide

How-To Topics for UI Automation Clients

IUIAutomation interface

IUIAutomationElement interface

IUIAutomationElementArray interface

IUIAutomationTreeWalker interface

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Similar Content

    • bobmcrae
      By bobmcrae
      I am experimenting with UIAWrappers.au3 from junkew to complete an application which presents absolutely no control information using AutoIT Window Info.  While I am able to complete the form successfully, I am not happy with the speed.  As a benchmark, the simple Send method occurs in far less than 1-second, but the UIAutomation approach takes 3-seconds.  I am wondering whether performance gains may be achieved by specifying the controls more precisely; but I am unsure how to do that.  I was able to speed things up a bit by setting $UIA_DefaultWaitTime=0.  The controls of interest are 5-levels deep, as show in the simplespy output below.  It seems I do get faster response by specifying the target/top-level window, as show in the code below.  Any ideas?
      #include "UIAWrappers.au3" _UIA_setVar("Global.Debug", False) _UIA_setVar("Global.Debug.File", False) _UIA_setVar("Global.Highlight", False) _UIA_setVar("DPN","Title:=NC-stat DPNCheck Communicator;controltype:=UIA_WindowControlTypeId;class:=Window") _UIA_action('DPN','setFocus') _UIA_setVar("DPN.firstName","AutomationId:=txtFirstName") _UIA_setVar("DPN.lastName", "AutomationId:=txtLastName") _UIA_Action('DPN.lastName','setvalue','last name') _UIA_setVar("DPN.ID", "AutomationId:=txtSubjectId") _UIA_setVar("DPN.DOB", "AutomationId:=PART_TextBox") _UIA_setVar("DPN.Ft", "AutomationId:=txtSubjectHeight") _UIA_setVar("DPN.In", "AutomationId:=txtSubjectHeight2") _UIA_Action('DPN.firstName','setvalue','first name') _UIA_Action('DPN.ID','setvalue','ID012345') _UIA_Action('DPN.DOB','setvalue','1/31/1932') _UIA_Action('DPN.Ft','setvalue','6') _UIA_Action('DPN.In','setvalue','1') SimpleSpy output:
      ;~ *** Standard code *** #include "UIAWrappers.au3" AutoItSetOption("MustDeclareVars", 1) Local $oP4=_UIA_getObjectByFindAll($UIA_oDesktop, "Title:=NC-stat DPNCheck Communicator;controltype:=UIA_WindowControlTypeId;class:=Window", $treescope_children) _UIA_Action($oP4,"setfocus") Local $oP3=_UIA_getObjectByFindAll($oP4, "Title:=;controltype:=UIA_PaneControlTypeId;class:=Frame", $treescope_children) Local $oP2=_UIA_getObjectByFindAll($oP3, "Title:=;controltype:=UIA_TabControlTypeId;class:=TabControl", $treescope_children) Local $oP1=_UIA_getObjectByFindAll($oP2, "Title:=Patient;controltype:=UIA_TabItemControlTypeId;class:=TabItem", $treescope_children) Local $oP0=_UIA_getObjectByFindAll($oP1, "Title:=;controltype:=UIA_PaneControlTypeId;class:=Frame", $treescope_children) ;~ First find the object in the parent before you can do something ;~$oUIElement=_UIA_getObjectByFindAll(".mainwindow", "title:=;ControlType:=UIA_EditControlTypeId", $treescope_subtree) Local $oUIElement=_UIA_getObjectByFindAll($oP0, "title:=;ControlType:=UIA_EditControlTypeId", $treescope_subtree) _UIA_action($oUIElement,"click")  
    • jackchen
      By jackchen
      OK,I've made my "Chrome_KeepLastTab.au3" work.This script adds some features to Chrome browser:
      1. Double click on a tab to close the tab.
      2. Keep last tab:This script monitors your mouse clicks and hotkeys,if you are about to close the last tab within Chrome(click close button on the last tab, middle click/double click on the last tab or press Ctrl + w or Ctrl + {F4}), a new tab will be open and then the old tab be closed.
      #include <WindowsConstants.au3> #include <WinAPI.au3> #include "CUIAutomation2.au3" #AutoIt3Wrapper_UseX64=Y ;Should be used for stuff like tagpoint having right struct etc. when running on a 64 bits os ConsoleWrite("@OSArch: " & @OSArch & ", @AutoItX64: " & @AutoItX64 & @CRLF) Global $DoubleClickTime = 500 Global $UIA_oUIAutomation ; The main library core CUI automation reference Global $hMouseEvent, $hMouseHook Global $aMouseEvent[2] Global $KeepLastTab = True ; settings from ini file If $KeepLastTab Then ;The main object with acces to the windows automation api 3.0 $UIA_oUIAutomation = ObjCreateInterface($sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation) If IsObj($UIA_oUIAutomation) Then HotKeySet("^w", "Hotkey_Event") ; Ctrl + w to close tab HotKeySet("^{F4}", "Hotkey_Event") ; Ctrl + {F4} to close tab $DoubleClickTime = DllCall("user32.dll", "uint", "GetDoubleClickTime")[0] OnAutoItExitRegister("UnhookMouse") ; Register mouse events callback $hMouseEvent = DllCallbackRegister("Mouse_Event", "int", "int;ptr;ptr") $hMouseHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hMouseEvent), _WinAPI_GetModuleHandle(0)) EndIf EndIf While 1 Sleep(100) WEnd ToolTip("") ; https://www.autoitscript.com/forum/topic/103362-monitoring-mouse-events/ Func Mouse_Event($nCode, $wParam, $lParam) Local $info, $mouseData, $time, $timeDiff If $nCode < 0 Then Return _WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam) ; Continue processing EndIf $tPoint = _WinAPI_GetMousePos() $hWnd = _WinAPI_WindowFromPoint($tPoint) ; if mouse is on the widget window(class: Chrome_RenderWidgetHostHWND), ; use $hWnd = _WinAPI_GetParent($hWnd) to get the parent Chrome window If Not StringInStr(_WinAPI_GetClassName($hWnd), "Chrome_WidgetWin_") Then ; Chrome_WidgetWin_1: Chrome window ; Chrome_WidgetWin_2: Chrome menu ; ignore non Chrome window Return _WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam) EndIf ToolTip($hWnd & " - " & _WinAPI_GetClassName($hWnd)) ;$tagPOINT = "struct;long X;long Y;endstruct" Local Const $MSLLHOOKSTRUCT = $tagPOINT & ";dword mouseData;dword flags;dword time;ulong_ptr dwExtraInfo" $info = DllStructCreate($MSLLHOOKSTRUCT, $lParam) $mouseData = DllStructGetData($info, 3) $time = DllStructGetData($info, 5) $timeDiff = $time - $aMouseEvent[1] Local $block Switch $wParam Case $WM_LBUTTONUP $aMouseEvent[1] = $time If $aMouseEvent[0] = "LClick" And ($timeDiff) < $DoubleClickTime Then $aMouseEvent[0] = "LDClick" Else $aMouseEvent[0] = "LClick" EndIf $block = KeepLastTab($hWnd, $aMouseEvent[0]) Case $WM_MBUTTONUP $aMouseEvent[1] = $time If $aMouseEvent[0] = "MClick" And ($timeDiff) < $DoubleClickTime Then $aMouseEvent[0] = "MDClick" Else $aMouseEvent[0] = "MClick" $block = KeepLastTab($hWnd, $aMouseEvent[0]) EndIf EndSwitch If Not $block Then _WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam) ; Continue processing EndIf EndFunc ;==>Mouse_Event Func UnhookMouse() _WinAPI_UnhookWindowsHookEx($hMouseHook) $hMouseHook = 0 DllCallbackFree($hMouseEvent) $hMouseEvent = 0 EndFunc ;==>UnhookMouse Func Hotkey_Event() Local $block Local $hWnd = WinGetHandle(WinGetTitle("[ACTIVE]")) If StringInStr(_WinAPI_GetClassName($hWnd), "Chrome_WidgetWin_1") Then ; Chrome_WidgetWin_1, Chrome window $block = KeepLastTab($hWnd, "Hotkey") EndIf If Not $block Then HotKeySet("^w") Send(@HotKeyPressed) HotKeySet("^w", "Hotkey_Event") EndIf EndFunc Func KeepLastTab($hWnd, $action = "LClick") ; Possible $action value: LClick, LDClick, MClick, Hotkey ConsoleWrite(@CRLF & $action & " on a Chrome window: " & $hWnd & @CRLF) Local $aMousePos = MouseGetPos() Local $pChrome, $oChrome $UIA_oUIAutomation.ElementFromHandle($hWnd, $pChrome) ; Window $oChrome = ObjCreateInterface($pChrome, $sIID_IUIAutomationElement, $dtagIUIAutomationElement) If Not IsObj($oChrome) Then ConsoleWrite("Failed to get Chrome object from hWnd." & @CRLF) Return EndIf ;$UIA_ControlTypePropertyId = 30003 $oChromeTabs = UIA_getFirstElement($oChrome, $UIA_ControlTypePropertyId, $UIA_TabControlTypeId, $treescope_subtree) If Not IsObj($oChromeTabs) Then ConsoleWrite("Failed to get Chrome tab bar object." & @CRLF) Return EndIf Local $t $oChromeTabs.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $t) If UBound($t) < 4 Then Return If $action <> "Hotkey" And ($aMousePos[0] < $t[0] Or $aMousePos[0] > $t[2]+$t[0] Or $aMousePos[1] < $t[1] Or $aMousePos[1] > $t[3]+$t[1]) Then ; Mouse not on tabs bar ConsoleWrite("Mouse is not on the tab bar. Ignore and return..." & @CRLF) Return EndIf Local $pTrueCondition, $pElements, $iLength, $oAutomationElementArray $UIA_oUIAutomation.CreateTrueCondition($pTrueCondition) $oCondition = ObjCreateInterface($pTrueCondition, $sIID_IUIAutomationCondition, $dtagIUIAutomationCondition) If Not IsObj($oCondition) Then Return $oChromeTabs.FindAll($treescope_children, $oCondition, $pElements) $oAutomationElementArray = ObjCreateInterface($pElements, $sIID_IUIAutomationElementArray, $dtagIUIAutomationElementArray) If Not IsObj($oAutomationElementArray) Then ConsoleWrite("Failed to find all Chrome tabs. " & @CRLF) Return EndIf $oAutomationElementArray.Length($iLength) Local $UIA_pUIElement, $oTab2 Local $iTabs = $iLength - 1 If $iTabs > 1 Then ; more than one tab If $action = "LDClick" Then ConsoleWrite("There are " & $iTabs & " tabs within Chrome window. " & @CRLF) For $i = 1 To $iTabs $oAutomationElementArray.GetElement($i, $UIA_pUIElement) $oTab2 = ObjCreateInterface($UIA_pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement) If Not IsObj($oTab2) Then ContinueLoop $oTab2.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $t) If UBound($t) < 4 Then ContinueLoop If $aMousePos[0] >= $t[0] And $aMousePos[0] <= $t[2]+$t[0] And $aMousePos[1] >= $t[1] And $aMousePos[1] <= $t[3]+$t[1] Then ConsoleWrite("You double clicked on one of " & $iTabs & " tabs. Close the tab and return..." & @CRLF) HotKeySet("^w") Send("^w") HotKeySet("^w", "Hotkey_Event") Return True EndIf Next EndIf Return EndIf ConsoleWrite("There is ONLY one tab within Chrome window. " & @CRLF) $oAutomationElementArray.GetElement(1, $UIA_pUIElement) $oTab = ObjCreateInterface($UIA_pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement) If Not IsObj($oTab) Then ConsoleWrite("Failed to get the last tab object." & @CRLF) Return EndIf Local $rtTab $oTab.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $rtTab) If UBound($rtTab) < 4 Then Return If $action <> "Hotkey" And ($aMousePos[0] < $rtTab[0] Or $aMousePos[0] > $rtTab[2]+$rtTab[0] Or $aMousePos[1] < $rtTab[1] Or $aMousePos[1] > $rtTab[3]+$rtTab[1]) Then ; Mouse not on the last tab ConsoleWrite("Mouse is not on the last tab. Ignore and return..." & @CRLF) Return EndIf If $action = "LClick" Then $oTabClose = UIA_getFirstElement($oTab, $UIA_ControlTypePropertyId, $UIA_ButtonControlTypeId, $treescope_subtree) If Not IsObj($oTabClose) Then ConsoleWrite("Failed to get the last tab close object." & @CRLF) Return EndIf $oTabClose.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $t) If UBound($t) < 4 Then Return If $aMousePos[0] < $t[0] Or $aMousePos[0] > $t[2]+$t[0] Or $aMousePos[1] < $t[1] Or $aMousePos[1] > $t[3]+$t[1] Then ; Mouse not on the tab close button Return EndIf EndIf ; open a new tab within chrome ConsoleWrite("The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing." & @CRLF) Send("^t") ConsoleWrite("A new tab created!" & @CRLF) Sleep(100) ConsoleWrite("Close the old tab and return..." & @CRLF) ;~ Local $pPattern ;~ $oTabClose.GetCurrentPattern($UIA_InvokePatternId, $pPattern) ;~ $oPattern = ObjCreateInterface($pPattern, $sIID_IUIAutomationInvokePattern, $dtagIUIAutomationInvokePattern) ;~ If IsObj($oPattern) Then ;~ ConsoleWrite("Invoke to close the tab..." & @CRLF) ;~ $oTabClose.SetFocus() ;~ $oPattern.Invoke() ;~ EndIf _WinAPI_UnhookWindowsHookEx($hMouseHook) $aMousePos = MouseGetPos() If $aMousePos[0] >= $rtTab[0] And $aMousePos[0] <= $rtTab[2]+$rtTab[0] And $aMousePos[1] >= $rtTab[1] And $aMousePos[1] <= $rtTab[3]+$rtTab[1] Then MouseClick("middle", $aMousePos[0], $aMousePos[1], 1, 0) Else MouseClick("middle", $rtTab[0]+$rtTab[2]/2, $rtTab[1]+$rtTab[3]/2, 1, 0) ; close the tab MouseMove($aMousePos[0], $aMousePos[1], 0) ; move mouse back to previous position EndIf ;~ _WinAPI_Mouse_Event($MOUSEEVENTF_MIDDLEDOWN, $rtTab[0]+$rtTab[2]/2, $rtTab[1]+$rtTab[3]/2) ;~ Sleep(100) ;~ _WinAPI_Mouse_Event($MOUSEEVENTF_MIDDLEUP, $rtTab[0]+$rtTab[2]/2, $rtTab[1]+$rtTab[3]/2) $hMouseHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hMouseEvent), _WinAPI_GetModuleHandle(0)) Return True ; to block mouse click/hot key EndFunc Func UIA_getFirstElement($obj, $propertyID, $tval, $treeScope) Local $pCondition, $oCondition $UIA_oUIAutomation.CreatePropertyCondition($propertyID, $tval, $pCondition) $oCondition = ObjCreateInterface($pCondition, $sIID_IUIAutomationPropertyCondition, $dtagIUIAutomationPropertyCondition) Local $UIA_oUIElement, $UIA_pUIElement $t = $obj.Findfirst($treeScope, $oCondition, $UIA_pUIElement) $UIA_oUIElement = ObjCreateInterface($UIA_pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement) If IsObj($UIA_oUIElement) Then Return $UIA_oUIElement Return SetError(1, 0, "") EndFunc ;==>UIA_getFirstElement My question is related to #AutoIt3Wrapper_UseX64 when run this script on 64-bit Win 7.
      No mater I set #AutoIt3Wrapper_UseX64=Y or #AutoIt3Wrapper_UseX64=N, this script works very well on hotkey event, while mouse clicks sometimes works if #AutoIt3Wrapper_UseX64=N and sometimes works on Y. Can some one test this and finger out what's wrong?
      Info from SciTe if #AutoIt3Wrapper_UseX64=Y :
      @OSArch: X64, @AutoItX64: 1 Hotkey on a Chrome window: 0x0000000000140330 There is ONLY one tab within Chrome window. The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing. A new tab created! Close the old tab and return... LClick on a Chrome window: 0x0000000000140330 There is ONLY one tab within Chrome window. The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing. A new tab created! Close the old tab and return... Info if #AutoIt3Wrapper_UseX64=N or comment out this line:
      @OSArch: X64, @AutoItX64: 0 Hotkey on a Chrome window: 0x00140330 There is ONLY one tab within Chrome window. The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing. A new tab created! Close the old tab and return... LClick on a Chrome window: 0x00140330 Failed to get Chrome tab bar object. Hotkey events and mouse click events share the same function KeepLastTab($hWnd, $action = "LClick"),Why this function triggered by hotkey works on both 32-bit and 64-bit while that triggered by mouse events failed on 32-bit autoit?