├── Examples ├── Example1.ahk ├── Example2.ahk ├── Example3_CaretPosition.ahk ├── Example4_FocusEvent.ahk ├── Example5_VSCode_LineColumn.ahk ├── Example6.ahk ├── Example7_Page_URL_From_Hover.ahk ├── Example8_Conditions.ahk └── Example9_SystemEvents.ahk ├── LICENSE ├── Lib └── Acc.ahk └── README.md /Examples/Example1.ahk: -------------------------------------------------------------------------------- 1 | #include ..\Lib\Acc.ahk 2 | #Requires AutoHotkey v2.0-a 3 | 4 | Run("notepad.exe") 5 | WinWaitActive("ahk_exe notepad.exe") 6 | oAcc := Acc.ElementFromHandle("ahk_exe notepad.exe") 7 | A_Clipboard := oAcc.DumpAll() 8 | MsgBox("Notepad element dumped into clipboard!") -------------------------------------------------------------------------------- /Examples/Example2.ahk: -------------------------------------------------------------------------------- 1 | #include ..\Lib\Acc.ahk 2 | #Requires AutoHotkey v2.0-a 3 | 4 | Run("notepad.exe") 5 | WinWaitActive("ahk_exe notepad.exe") 6 | oAcc := Acc.ElementFromHandle("ahk_exe notepad.exe") 7 | oEditor := oAcc[4,1,4] ; Can get the element by path 8 | 9 | ; If the editor element wasn't found, then we are probably dealing with Windows 11 10 | if oEditor.RoleText != "editable text" { 11 | oEditor := oAcc.FindElement({RoleText:"editable text"}) 12 | Win11 := True 13 | } 14 | oEditor.Highlight() 15 | oEditor.Value := "Example text" ; Set the value 16 | 17 | oAcc.FindElement({Name:"File"}).DoDefaultAction() 18 | ; Windows 11 menu is in another window... 19 | if IsSet(Win11) { 20 | WinWait("PopupHost ahk_exe notepad.exe") 21 | oAcc := Acc.ElementFromHandle() 22 | } 23 | oAcc.WaitElementExist({Name:"save as", casesensitive:False, matchmode:"StartsWith"}).DoDefaultAction() ; Wait "Save As" to exist, matching the start of string, case insensitive -------------------------------------------------------------------------------- /Examples/Example3_CaretPosition.ahk: -------------------------------------------------------------------------------- 1 | #include ..\Lib\Acc.ahk 2 | #Requires AutoHotkey v2.0-a 3 | 4 | oCaretPos := {x:0,y:0} 5 | Loop { 6 | try oCaretPos := Acc.ElementFromHandle("A", Acc.ObjId.Caret).Location 7 | if oCaretPos.x=0 && oCaretPos.y=0 8 | CaretGetPos(&x, &y), ToolTip("x: " x " y: " y) 9 | else 10 | ToolTip("x: " oCaretPos.x " y: " oCaretPos.y) 11 | } -------------------------------------------------------------------------------- /Examples/Example4_FocusEvent.ahk: -------------------------------------------------------------------------------- 1 | #include ..\Lib\Acc.ahk 2 | #Requires AutoHotkey v2.0-a 3 | 4 | Persistent() 5 | 6 | ; This won't work without assigning the registered event object to a variable 7 | ; because the event will be deregistered once the object is destroyed (and not assigning it to 8 | ; any thing will cause it to be destroyed automatically) 9 | h := Acc.RegisterWinEvent(OnFocus, Acc.Event.Object_Focus) 10 | h2 := Acc.RegisterWinEvent(OnMoveSizeStart, Acc.Event.System_MoveSizeStart) 11 | 12 | OnFocus(oAcc, info) { 13 | try ToolTip("Element: " oAcc.Name "`nEvent: " Acc.Event[info.Event] "`nTime: " info.EventTime 14 | . "`nSender WinID: " info.WinID "`nSender ControlID: " info.ControlID) 15 | } 16 | OnMoveSizeStart(oAcc, info) { 17 | try ToolTip("Element: " oAcc.Name "`nEvent: " Acc.Event[info.Event]) 18 | } 19 | -------------------------------------------------------------------------------- /Examples/Example5_VSCode_LineColumn.ahk: -------------------------------------------------------------------------------- 1 | #include ..\Lib\Acc.ahk 2 | #Requires AutoHotkey v2.0-a 3 | 4 | Loop { 5 | lnCol := GetVSCodeLnCol() 6 | ToolTip("Ln " lnCol.Ln ", Col " lnCol.Col "`nFound elements path: " lnCol.Path) 7 | } 8 | GetVSCodeLnCol() { 9 | static oLnCol 10 | try RegExMatch(oLnCol.Name, "Ln (\d+), Col (\d+)", &match) 11 | catch { 12 | oLnCol := Acc.ElementFromChromium("ahk_exe Code.exe").FindElement({Name:"Ln \d+, Col \d+", matchmode:"Regex"}) 13 | RegExMatch(oLnCol.Name, "Ln (\d+), Col (\d+)", &match) 14 | } 15 | return {Ln:match[1], Col:match[2], Path:oLnCol.Path} 16 | } -------------------------------------------------------------------------------- /Examples/Example6.ahk: -------------------------------------------------------------------------------- 1 | #include ..\Lib\Acc.ahk 2 | #Requires AutoHotkey v2.0-a 3 | 4 | SetTitleMatchMode(2) 5 | 6 | Run("explore C:\") 7 | CDriveName := DriveGetLabel("C:") " (C:)" 8 | WinWaitActive(CDriveName) 9 | 10 | oExplorer := Acc.ElementFromHandle() 11 | oExplorer.FindElement({Name:"Program Files", RoleText:"list item", matchmode:"Substring"}).Select("AddSelection") 12 | (oWin := oExplorer.FindElement({Name:"Windows", RoleText:"list item"})).Select(Acc.SelectionFlag.AddSelection) 13 | 14 | selectedItems := "" 15 | for i, selected in oWin.Parent.Selection 16 | selectedItems .= "Selection " i ": " selected.Dump() "`n" 17 | 18 | MsgBox(selectedItems) 19 | -------------------------------------------------------------------------------- /Examples/Example7_Page_URL_From_Hover.ahk: -------------------------------------------------------------------------------- 1 | #include ..\Lib\Acc.ahk 2 | #Requires AutoHotkey v2.0-a 3 | 4 | ; Tries to return the URL from the document element of a web browser. 5 | ; Hover over a browser with the cursor to test it out. 6 | 7 | Loop { 8 | oAcc := Acc.ElementFromPoint(), oEl := "" 9 | try { 10 | oEl := oAcc.Normalize({Role:15, not:{Value:""}}) 11 | RoleText := "`"`"", Name := " `"`"" 12 | try RoleText := "`"" oAcc.RoleText "`"" 13 | try Name := " `"" oAcc.Name "`"" 14 | ToolTip("Document element value: " oEl.Value "`nActual element under mouse: " RoleText Name) 15 | } 16 | } -------------------------------------------------------------------------------- /Examples/Example8_Conditions.ahk: -------------------------------------------------------------------------------- 1 | #include ..\Lib\Acc.ahk 2 | #Requires AutoHotkey v2.0-a 3 | 4 | Run("notepad.exe") 5 | WinWaitActive("ahk_exe notepad.exe") 6 | oAcc := Acc.ElementFromHandle("ahk_exe notepad.exe") 7 | 8 | ; And condition is created with {} 9 | MsgBox("First element matching Name `"Maximize`" and Role `"12`":`n" 10 | . oAcc.FindElement({Name:"Maximize", Role:12}).Dump()) 11 | 12 | ; Or condition is created with [] 13 | result := "All elements matching either Name `"Minimize`" or Name `"Maximize`":`n`n" 14 | for i, v in oAcc.FindElements([{Name:"Minimize"}, {Name:"Maximize"}]) 15 | result .= i ": " v.Dump() "`n" 16 | MsgBox result 17 | 18 | ; Negate with "not" 19 | result := "All elements matching Name `"Minimize`":`n`n" 20 | for i, v in oAcc.FindElements({Name:"Minimize"}) 21 | result .= i ": " v.Dump() "`n" 22 | result .= "`nAll elements matching Name `"Minimize`" but not Role `"43`":`n`n" 23 | for i, v in oAcc.FindElements({Name:"Minimize", not:{Role:43}}) 24 | result .= i ": " v.Dump() "`n" 25 | MsgBox result 26 | 27 | ; Specify case-sensitivity with "casesensitive" or "cs", and matchmode with "matchmode" or "mm". 28 | ; Case-sensitivity is ignored with numeric values. 29 | result := "All elements matching Name `"minim`" (case insensitive, anywhere in string):`n`n" 30 | for i, v in oAcc.FindElements({Name:"minim", cs:false, mm:2}) ; mm:2 is equal to mm:"SubString" 31 | result .= i ": " v.Dump() "`n" 32 | MsgBox result 33 | 34 | ; Find nth element by specifying key "index" or "i". Negative index reverses the search direction. 35 | result := "All elements matching Role `"9`":`n`n" 36 | for i, v in oAcc.FindElements({Role:9},5) 37 | result .= i ": " v.Dump() "`n" 38 | result .= "`nLast element matching Role `"9`":`n" 39 | result .= oAcc.FindElement({Role:9, index:-1}).Dump() 40 | result .= "`nSecond element matching Role `"9`":`n" 41 | result .= oAcc.FindElement({Role:9, index:2}).Dump() 42 | MsgBox result 43 | 44 | ; To use "or" condition with other options (such as "index"), put it inside an object with key "or" 45 | MsgBox("Last element matching Name `"Save Ctrl+S`" or Name `"Save As... Ctrl+Shift+S`":`n" 46 | . oAcc.FindElement({or:[{Name:"Save Ctrl+S"}, {Name:"Save As... Ctrl+Shift+S"}], index:-1}).Dump()) 47 | 48 | ; To find specific element object (for example to determine the path) use IsEqual 49 | oEdit := oAcc[4,1,4] 50 | MsgBox("oEdit element: " oEdit.Dump() 51 | . "`n`nFirst element matching oEdit: " oAcc.FindElement({IsEqual:oEdit}).Dump() 52 | . "`n`nFound elements path: " oAcc.FindElement({IsEqual:oEdit}).Path) 53 | 54 | ; Conditions can be used inside paths as well. 55 | MsgBox("oEdit element (4th element with State 1048576, then 1st element, then 1st element with Name `"Text Editor`"): `n`n" 56 | . oAcc[{State:1048576, i:4},1,{Name:"Text Editor"}].Dump()) -------------------------------------------------------------------------------- /Examples/Example9_SystemEvents.ahk: -------------------------------------------------------------------------------- 1 | #include ..\Lib\Acc.ahk 2 | #Requires AutoHotkey v2.0-a 3 | 4 | Persistent() 5 | 6 | global handler 7 | 8 | MsgBox("Press F1 to start capturing all system events.`nPress F2 to stop capturing.`nPress Esc to exit app.") 9 | 10 | /* 11 | This function will be called by Acc when a system event happens. 12 | Multiple events can be registered to one callback function. 13 | If we were interested only in getting oAcc, we could use OnSystemEvent(oAcc, *) instead. 14 | */ 15 | OnSystemEvent(oAcc, info) { 16 | try { 17 | ToolTip("Element: " oAcc.Name "`nEvent: " Acc.Event[info.Event] "`nTime: " info.EventTime 18 | . "`nSender window: " WinGetTitle(info.WinID)) 19 | SetTimer(ToolTip, 2000) 20 | } 21 | } 22 | /* 23 | Check whether the system events have already been registered to the handler variable. 24 | If not, then register an event range from EVENT.SYSTEM_SOUND to EVENT.SYSTEM_MINIMIZEEND, 25 | which will also include all the event inbetween those two (eg SYSTEM_CAPTURESTART). 26 | 27 | Note that the events will be deregistered once the returned handler object is destroyed, 28 | so we always need to store it in a variable, in this case the "handler" variable. 29 | Also it can't be a local variable, because local variables get destroyed after 30 | exiting the function/hotkey. 31 | */ 32 | F1:: 33 | { 34 | global handler 35 | if !IsSet(handler) 36 | handler := Acc.RegisterWinEvent(OnSystemEvent, Acc.Event.System_Sound, Acc.Event.System_MinimizeEnd) 37 | } 38 | ; Deregistering the events will happen when the event handler object is destroyed, for example by unsetting it 39 | F2::global handler := unset 40 | Esc::ExitApp -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Descolada 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Lib/Acc.ahk: -------------------------------------------------------------------------------- 1 | /* 2 | Accessibility library for AHK v2 3 | 4 | Authors: Sean, jethrow, Sancarn (v1 code), Descolada 5 | Special thanks to Lexikos for many tips about v2 6 | 7 | Short introduction: 8 | Acc v2 in not a port of v1, but instead a complete redesign to incorporate more object-oriented approaches. 9 | 10 | Notable changes: 11 | 1) All Acc elements are now array-like objects, where the "Length" property contains the number of children, any nth children can be accessed with element[n], and children can be iterated over with for loops. 12 | 2) Acc main functions are contained in the global Acc object 13 | 3) Element methods are contained inside element objects 14 | 4) Element properties can be used without the "acc" prefix 15 | 5) ChildIds have been removed (are handled in the backend), but can be accessed through 16 | Element.ChildId 17 | 6) Additional methods have been added for elements, such as FindElement, FindElements, Click 18 | 7) Acc constants are included in the main Acc object 19 | 8) AccViewer is built into the library: when ran directly the AccViewer will show, when included 20 | in another script then it won't show (but can be opened by calling Acc.Viewer()) 21 | 22 | Acc constants/properties: 23 | Constants can be accessed as properties (eg Acc.ObjId.Caret), or the property name can be 24 | fetched by getting as an item (eg Acc.ObjId[0xFFFFFFF8]) 25 | 26 | ObjId - object identifiers that identify categories of accessible objects within a window. 27 | State - used to describe the state of objects in an application UI. These are returned by Element.State or Element.StateText. 28 | Role - used to describe the roles of various UI objects in an application. These are returned by Element.Role or Element.RoleText. 29 | NavDir - indicate the spatial (up, down, left, and right) or logical (first child, 30 | last, next, and previous) direction used with Element.Navigate() to navigate from one 31 | user interface element to another within the same container. 32 | SelectionFlag - used to specify how an accessible object becomes selected or takes the focus. 33 | These are used by Element.Select(). 34 | Event - events that are generated by the operating system and by applications. These are 35 | used when dealing with RegisterWinEvent. 36 | 37 | More thorough explanations for the constants are available in Microsoft documentation: 38 | https://docs.microsoft.com/en-us/windows/win32/winauto/constants-and-enumerated-types 39 | 40 | Acc methods: 41 | ElementFromPoint(x:=unset, y:=unset, activateChromium := True) 42 | Gets an Acc element from screen coordinates X and Y (NOT relative to the active window). 43 | ElementFromHandle(hWnd:="", idObject := "Window", activateChromium := True) 44 | Gets an Acc element from a WinTitle, by default the Last Found Window. 45 | Additionally idObject can be specified from Acc.ObjId constants (eg to get the Caret location). 46 | ElementFromChromium(hWnd:="", activateChromium := True) 47 | Gets an Acc element for the Chromium render control, by default for the Last Found Window. 48 | GetRootElement() 49 | Gets the Acc element for the Desktop 50 | ActivateChromiumAccessibility(hWnd:="") 51 | Sends the WM_GETOBJECT message to the Chromium document element and waits for the 52 | app to be accessible to Acc. This is called when ElementFromPoint or ElementFromHandle 53 | activateChromium flag is set to True. A small performance increase may be gotten 54 | if that flag is set to False when it is not needed. 55 | RegisterWinEvent(event, callback, PID:=0) 56 | RegisterWinEvent(eventMin, eventMax, callback, PID:=0) 57 | Registers an event or event range from Acc.Event to a callback function and returns 58 | a new object containing the WinEventHook 59 | EventMax is an optional variable: if only eventMin and callback are provided, then 60 | only that single event is registered. If all three arguments are provided, then 61 | an event range from eventMin to eventMax are registered to the callback function. 62 | The callback function needs to have two arguments: 63 | CallbackFunction(oAcc, EventInfo) 64 | 65 | When the callback function is called: 66 | oAcc will be the Acc element that called the event 67 | EventInfo will be an object containing the following properties: 68 | Event - an Acc.Event constant 69 | EventTime - when the event was triggered in system time 70 | WinID - handle of the window that sent the event 71 | ControlID - handle of the control that sent the event, which depending on the 72 | window will be the window itself or a control 73 | ObjId - the object Id (Acc.ObjId) the event was called with 74 | PID is the Process ID of the process/window the events will be registered from. By default 75 | events from all windows are registered. 76 | Unhooking of the event handler will happen once the returned object is destroyed 77 | (either when overwritten by a constant, or when the script closes). 78 | ClearHighlights() 79 | Removes all highlights created by IAccessible.Highlight 80 | 81 | Legacy methods: 82 | SetWinEventHook(eventMin, eventMax, pCallback) 83 | UnhookWinEvent(hHook) 84 | ElementFromPath(ChildPath, hWnd:="A") => Same as ElementFromHandle[comma-separated path] 85 | GetRoleText(nRole) => Same as element.RoleText 86 | GetStateText(nState) => Same as element.StateText 87 | Query(pAcc) => For internal use 88 | 89 | IAccessible element properties: 90 | Element[n] => Gets the nth element. Multiple of these can be used like a path: 91 | Element[4,1,4] will select 4th childs 1st childs 4th child 92 | Path string can also be used with comma-separated numbers or RoleText 93 | Element["4,window,4"] will select 4th childs first RoleText=window childs 4th child 94 | Element["4,window2,4"] will select the second RoleText=window 95 | With a path string "p" followed by a number n will return the nth parent. 96 | Element["p2,2"] will get the parent parents second child 97 | Conditions (see ValidateCondition) are supported: 98 | Element[4,{Name:"Something"}] will select the fourth childs first child matching the name "Something" 99 | Conditions also accept an index (or i) parameter to select from multiple similar elements 100 | Element[{Name:"Something", i:3}] selects the third element of elements with name "Something" 101 | Negative index will select from the last element 102 | Element[{Name:"Something", i:-1}] selects the last element of elements with name "Something" 103 | Since index/i needs to be a key-value pair, then to use it with an "or" condition 104 | it must be inside an object ("and" condition), for example with key "or": 105 | Element[{or:[{Name:"Something"},{Name:"Something else"}], i:2}] 106 | Name => Gets or sets the name. All objects support getting this property. 107 | Value => Gets or sets the value. Not all objects have a value. 108 | Role => Gets the Role of the specified object in integer form. All objects support this property. 109 | RoleText => Role converted into text form. All objects support this property. 110 | Help => Retrieves the Help property string of an object. Not all objects support this property. 111 | KeyboardShortcut => Retrieves the specified object's shortcut key or access key. Not all objects support this property. 112 | State => Retrieves the current state in integer form. All objects support this property. 113 | StateText => State converted into text form 114 | Description => Retrieves a string that describes the visual appearance of the specified object. Not all objects have a description. 115 | DefaultAction => Retrieves a string that indicates the object's default action. Not all objects have a default action. 116 | Focus => Returns the focused child element (or itself). 117 | If no child is focused, an error is thrown 118 | Selection => Retrieves the selected children of this object. All objects that support selection must support this property. 119 | Parent => Returns the parent element. All objects support this property. 120 | IsChild => Checks whether the current element is of child type 121 | Length => Returns the number of children the element has 122 | Location => Returns the object's current screen location in an object {x,y,w,h} 123 | Children => Returns all children as an array (usually not required) 124 | Exists => Checks whether the element is still alive and accessible 125 | ControlID => ID (hwnd) of the control associated with the element 126 | WinID => ID (hwnd) of the window the element belongs to 127 | accessible => ComObject of the underlying IAccessible (for internal use) 128 | childId => childId of the underlying IAccessible (for internal use) 129 | 130 | IAccessible element methods: 131 | Select(flags) 132 | Modifies the selection or moves the keyboard focus of the specified object. 133 | flags can be any of the Acc.SelectionFlag constants 134 | DoDefaultAction() 135 | Performs the specified object's default action. Not all objects have a default action. 136 | GetNthChild(n) 137 | This is equal to element[n] 138 | GetPath(oTarget) 139 | Returns the path from the current element to oTarget element. 140 | The returned path is a comma-separated list of integers corresponding to the order the 141 | Acc tree needs to be traversed to access oTarget element from this element. 142 | If no path is found then an empty string is returned. 143 | GetLocation(relativeTo:="") 144 | Returns an object containing the x, y coordinates and width and height: {x:x coordinate, y:y coordinate, w:width, h:height}. 145 | relativeTo can be client, window or screen, default is A_CoordModeMouse. 146 | IsEqual(oCompare) 147 | Checks whether the element is equal to another element (oCompare) 148 | FindElement(condition, scope:=4, index:=1, order:=0, depth:=-1) 149 | Finds the first element matching the condition (see description under ValidateCondition). 150 | The returned element also has the "Path" property with the found elements path. 151 | 152 | Condition: A condition object (see ValidateCondition). This condition object can also contain named argument values: 153 | FindElement({Name:"Something", scope:"Subtree"}) 154 | Scope: the search scope (Acc.TreeScope value): Element, Children, Family (Element+Children), Descendants, SubTree (Element+Descendants). Default is Descendants. 155 | Index: can be used to search for i-th element. 156 | Like the other parameters, this can also be supplied in the condition with index or i: 157 | FindElement({Name:"Something", i:3}) finds the third element with name "Something" 158 | Negative index reverses the search direction: 159 | FindElement({Name:"Something", i:-1}) finds the last element with name "Something" 160 | Since index/i needs to be a key-value pair, then to use it with an "or" condition 161 | it must be inside an object ("and" condition), for example with key "or": 162 | FindElement({or:[{Name:"Something"}, {Name:"Something else"}], index:2}) 163 | Order: defines the order of tree traversal (Acc.TreeTraversalOptions value): 164 | Default, PostOrder, LastToFirst. Default is FirstToLast and PreOrder. 165 | FindElements(condition:=True, scope:=4, depth:=-1) 166 | Returns an array of elements matching the condition (see description under ValidateCondition) 167 | The returned elements also have the "Path" property with the found elements path 168 | By default matches any condition. 169 | WaitElement(conditionOrPath, timeOut:=-1, scope:=4, index:=1, order:=0, depth:=-1) 170 | Waits an element to be detectable in the Acc tree. This doesn't mean that the element 171 | is visible or interactable, use WaitElementExist for that. 172 | Timeout less than 1 waits indefinitely, otherwise is the wait time in milliseconds 173 | A timeout returns 0. 174 | WaitElementExist(conditionOrPath, timeOut:=-1, scope:=4, index:=1, order:=0, depth:=-1) 175 | Waits an element exist that matches a condition or a path. 176 | Timeout less than 1 waits indefinitely, otherwise is the wait time in milliseconds 177 | A timeout returns 0. 178 | WaitNotExist(timeOut:=-1) 179 | Waits for the element to not exist. 180 | Timeout less than 1 waits indefinitely, otherwise is the wait time in milliseconds 181 | A timeout returns 0. 182 | Normalize(condition) 183 | Checks whether the current element or any of its ancestors match the condition, 184 | and returns that element. If no element is found, an error is thrown. 185 | ValidateCondition(condition) 186 | Checks whether the element matches a provided condition. 187 | Everything inside {} is an "and" condition, or a singular condition with options 188 | Everything inside [] is an "or" condition 189 | "not" key creates a not condition 190 | "matchmode" key (short form: "mm") defines the MatchMode: StartsWith, Substring, Exact, RegEx (Acc.MATCHMODE values) 191 | "casesensitive" key (short form: "cs") defines case sensitivity: True=case sensitive; False=case insensitive 192 | Any other key (but recommended is "or") can be used to use "or" condition inside "and" condition. 193 | Additionally, when matching for location then partial matching can be used (eg only width and height) 194 | and relative mode (client, window, screen) can be specified with "relative" or "r". 195 | An empty object {} is used as "unset" or "N/A" value. 196 | 197 | For methods which use this condition, it can also contain named arguments: 198 | oAcc.FindElement({Name:"Something", scope:"Subtree", order:"LastToFirst"}) 199 | is equivalent to FindElement({Name:"Something"}, "Subtree",, "LastToFirst") 200 | is equivalent to FindElement({Name:"Something"}, Acc.TreeScope.SubTree,, Acc.TreeTraversalOptions.LastToFirst) 201 | is equivalent to FindElement({Name:"Something"}, 7,, 1) 202 | 203 | {Name:"Something"} => Name must match "Something" (case sensitive) 204 | {Name:"Something", matchmode:"SubString", casesensitive:False} => Name must contain "Something" anywhere inside the Name, case insensitive. matchmode:"SubString" == matchmode:2 == matchmode:Acc.MatchMode.SubString 205 | {Name:"Something", RoleText:"something else"} => Name must match "Something" and RoleText must match "something else" 206 | [{Name:"Something", Role:42}, {Name:"Something2", RoleText:"something else"}] => Name=="Something" and Role==42 OR Name=="Something2" and RoleText=="something else" 207 | {Name:"Something", not:[{RoleText:"something", mm:"Substring"}, {RoleText:"something else", cs:1}]} => Name must match "something" and RoleText cannot match "something" (with matchmode=Substring == matchmode=2) nor "something else" (casesensitive matching) 208 | {or:[{Name:"Something"},{Name:"Something else"}], or2:[{Role:20},{Role:42}]} 209 | {Location:{w:200, h:100, r:"client"}} => Location must match width 200 and height 100 relative to client 210 | Dump(scope:=1, delimiter:=" ", depth:=-1) 211 | {Name:{}} => Matches for no defined name (outputted by Dump as N/A) 212 | Outputs relevant information about the element (Name, Value, Location etc) 213 | Scope is the search scope (Acc.TreeScope value): Element, Children, Family (Element+Children), Descendants, SubTree (Element+Descendants). Default is Element. 214 | DumpAll(delimiter:=" ", depth:=-1) 215 | Outputs relevant information about the element and all descendants of the element. This is equivalent to Dump(7) 216 | Highlight(showTime:=unset, color:="Red", d:=2) 217 | Highlights the element for a chosen period of time 218 | Possible showTime values: 219 | Unset - highlights for 2 seconds, or removes the highlighting 220 | 0 - Indefinite highlighting. If the element object gets destroyed, so does the highlighting. 221 | Positive integer (eg 2000) - will highlight and pause for the specified amount of time in ms 222 | Negative integer - will highlight for the specified amount of time in ms, but script execution will continue 223 | "clear" - removes the highlight 224 | color can be any of the Color names or RGB values 225 | d sets the border width 226 | Click(WhichButton:="left", ClickCount:=1, DownOrUp:="", Relative:="", NoActivate:=False) 227 | Click the center of the element. 228 | If WhichButton is a number, then Sleep will be called with that number. 229 | Eg Click(200) will sleep 200ms after clicking 230 | If ClickCount is a number >=10, then Sleep will be called with that number. To click 10+ times and sleep after, specify "ClickCount SleepTime". Ex: Click("left", 200) will sleep 200ms after clicking. 231 | Ex: Click("left", "20 200") will left-click 20 times and then sleep 200ms. 232 | Relative can be offset values for the click (eg "-5 10" would offset the click from the center, X by -5 and Y by +10). 233 | NoActivate will cause the window not to be brought to focus before clicking if the clickable point is not visible on the screen. 234 | ControlClick(WhichButton:="left", ClickCount:=1, Options:="") 235 | ControlClicks the element after getting relative coordinates with GetLocation("client"). 236 | If WhichButton is a number, then a Sleep will be called afterwards. Ex: ControlClick(200) will sleep 200ms after clicking. Same for ControlClick("ahk_id 12345", 200) 237 | Navigate(navDir) 238 | Navigates in one of the directions specified by Acc.NavDir constants. Not all elements implement this method. 239 | HitTest(x, y) 240 | Retrieves the child element or child object that is displayed at a specific point on the screen. 241 | This shouldn't be used, since Acc.ElementFromPoint uses this internally 242 | 243 | 244 | Comments about design choices: 245 | 1) In this library accessing non-existant properties will cause an error to be thrown. 246 | This means that if AccViewer reports N/A as a value then the property doesn't exist, 247 | which is different from the property being "" or 0. Because of this, we have another 248 | way of differentiating/filtering elements, which may or may not be useful. 249 | 2) Methods that have Hwnd as an argument have the default value of Last Found Window. 250 | This is to be consistent with AHK overall. 251 | 3) Methods that will return a starting point Acc element usually have the activateChromium 252 | option set to True. This is because Chromium-based applications are quite common, but 253 | interacting with them via Acc require accessibility to be turned on. It makes sense to 254 | detect those windows automatically and activate by default (if needed). 255 | 4) ElementFromHandle: in AHK it may make more sense for idObject to have the default value of 256 | -4 (Acc.ObjId.Client), but using that by default will prevent access from the window title bar, 257 | which might be useful to have. Usually though, ObjId.Client will be enough, and when 258 | using control Hwnds then it must be used (otherwise the object for the window will be returned). 259 | */ 260 | 261 | #DllLoad oleacc 262 | ;DllCall("SetThreadDpiAwarenessContext", "ptr", -4, "ptr") ; For multi-monitor setups, otherwise coordinates might be reported wrong. 263 | 264 | if (!A_IsCompiled and A_LineFile=A_ScriptFullPath) 265 | Acc.Viewer() 266 | 267 | class Acc { 268 | class Enumeration { 269 | ; This is enables getting property names from values using the array style 270 | __Item[param] { 271 | get { 272 | local k, v 273 | if !this.HasOwnProp("__CachedValues") { 274 | this.__CachedValues := Map() 275 | for k, v in this.OwnProps() 276 | this.__CachedValues[v] := k 277 | } 278 | if this.__CachedValues.Has(param) 279 | return this.__CachedValues[param] 280 | throw UnsetItemError("Property item `"" param "`" not found!", -2) 281 | } 282 | } 283 | } 284 | 285 | static RegisteredWinEvents := Map(), MaxRecurseDepth := 0xFFFFFFFF 286 | 287 | ; MatchMode constants used in condition objects 288 | static MatchMode := { 289 | StartsWith:1, 290 | Substring:2, 291 | Exact:3, 292 | RegEx:"Regex", 293 | base:this.Enumeration.Prototype 294 | } 295 | 296 | ; Used wherever the scope variable is needed (eg Dump, FindElement, FindElements) 297 | static TreeScope := { 298 | Element:1, 299 | Children:2, 300 | Family:3, 301 | Descendants:4, 302 | Subtree:7, 303 | base:this.Enumeration.Prototype 304 | } 305 | 306 | Static TreeTraversalOptions := { 307 | Default:0, 308 | PostOrder:1, 309 | LastToFirst:2, 310 | PostOrderLastToFirst:3, 311 | base:this.Enumeration.Prototype 312 | } 313 | 314 | ;Https://Msdn.Microsoft.Com/En-Us/Library/Windows/Desktop/Dd373606(V=Vs.85).Aspx 315 | Static ObjId := { 316 | Window:0x00000000, 317 | SysMenu:0xffffffff, 318 | TitleBar:0xfffffffe, 319 | Menu:0xfffffffd, 320 | Client:0xfffffffc, 321 | VScroll:0xfffffffb, 322 | HScroll:0xfffffffa, 323 | SizeGrip:0xfffffff9, 324 | Caret:0xfffffff8, 325 | Cursor:0xfffffff7, 326 | Alert:0xfffffff6, 327 | Sound:0xfffffff5, 328 | QueryClassNameIdx:0xfffffff4, 329 | NativeOM:0xfffffff0, 330 | base:this.Enumeration.Prototype 331 | } 332 | 333 | ;Https://Msdn.Microsoft.Com/En-Us/Library/Windows/Desktop/Dd373609(V=Vs.85).Aspx 334 | Static State := { 335 | Normal:0, 336 | Unavailable:0x1, 337 | Selected:0x2, 338 | Focused:0x4, 339 | Pressed:0x8, 340 | Checked:0x10, 341 | Mixed:0x20, 342 | Indeterminate:0x20, 343 | ReadOnly:0x40, 344 | HotTracked:0x80, 345 | Default:0x100, 346 | Expanded:0x200, 347 | Collapsed:0x400, 348 | Busy:0x800, 349 | Floating:0x1000, 350 | Marqueed:0x2000, 351 | Animated:0x4000, 352 | Invisible:0x8000, 353 | Offscreen:0x10000, 354 | Sizeable:0x20000, 355 | Moveable:0x40000, 356 | SelfVoicing:0x80000, 357 | Focusable:0x100000, 358 | Selectable:0x200000, 359 | Linked:0x400000, 360 | Traversed:0x800000, 361 | MultiSelectable:0x1000000, 362 | ExtSelectable:0x2000000, 363 | Alert_Low:0x4000000, 364 | Alert_Medium:0x8000000, 365 | Alert_High:0x10000000, 366 | Protected:0x20000000, 367 | Valid:0x7fffffff, 368 | base:this.Enumeration.Prototype 369 | } 370 | 371 | ;Https://Msdn.Microsoft.Com/En-Us/Library/Windows/Desktop/Dd373608(V=Vs.85).Aspx 372 | Static Role := { 373 | TitleBar:0x1, 374 | MenuBar:0x2, 375 | ScrollBar:0x3, 376 | Grip:0x4, 377 | Sound:0x5, 378 | Cursor:0x6, 379 | Caret:0x7, 380 | Alert:0x8, 381 | Window:0x9, 382 | Client:0xa, 383 | MenuPopup:0xb, 384 | MenuItem:0xc, 385 | ToolTip:0xd, 386 | Application:0xe, 387 | Document:0xf, 388 | Pane:0x10, 389 | Chart:0x11, 390 | Dialog:0x12, 391 | Border:0x13, 392 | Grouping:0x14, 393 | Separator:0x15, 394 | Toolbar:0x16, 395 | StatusBar:0x17, 396 | Table:0x18, 397 | ColumnHeader:0x19, 398 | RowHeader:0x1a, 399 | Column:0x1b, 400 | Row:0x1c, 401 | Cell:0x1d, 402 | Link:0x1e, 403 | HelpBalloon:0x1f, 404 | Character:0x20, 405 | List:0x21, 406 | ListItem:0x22, 407 | Outline:0x23, 408 | OutlineItem:0x24, 409 | PageTab:0x25, 410 | PropertyPage:0x26, 411 | Indicator:0x27, 412 | Graphic:0x28, 413 | StaticText:0x29, 414 | Text:0x2a, 415 | PushButton:0x2b, 416 | CheckButton:0x2c, 417 | RadioButton:0x2d, 418 | ComboBox:0x2e, 419 | Droplist:0x2f, 420 | Progressbar:0x30, 421 | Dial:0x31, 422 | HotkeyField:0x32, 423 | Slider:0x33, 424 | SpinButton:0x34, 425 | Diagram:0x35, 426 | Animation:0x36, 427 | Equation:0x37, 428 | ButtonDropdown:0x38, 429 | ButtonMenu:0x39, 430 | ButtonDropdownGrid:0x3a, 431 | Whitespace:0x3b, 432 | PageTabList:0x3c, 433 | Clock:0x3d, 434 | SplitButton:0x3e, 435 | IPAddress:0x3f, 436 | OutlineButton:0x40, 437 | base:this.Enumeration.Prototype 438 | } 439 | ;Https://, Msdn.Microsoft.Com/En-Us/Library/Windows/Desktop/Dd373600(V=Vs.85).Aspx 440 | Static NavDir := { 441 | Min:0x0, 442 | Up:0x1, 443 | Down:0x2, 444 | Left:0x3, 445 | Right:0x4, 446 | Next:0x5, 447 | Previous:0x6, 448 | FirstChild:0x7, 449 | LastChild:0x8, 450 | Max:0x9, 451 | base:this.Enumeration.Prototype 452 | } 453 | 454 | ;Https://Msdn.Microsoft.Com/En-Us/Library/Windows/Desktop/Dd373634(V=Vs.85).Aspx 455 | Static SelectionFlag := { 456 | None:0x0, 457 | TakeFocus:0x1, 458 | TakeSelection:0x2, 459 | ExtendSelection:0x4, 460 | AddSelection:0x8, 461 | RemoveSelection:0x10, 462 | Valid:0x1f, 463 | base:this.Enumeration.Prototype 464 | } 465 | 466 | ;Msaa Events List: 467 | ; Https://Msdn.Microsoft.Com/En-Us/Library/Windows/Desktop/Dd318066(V=Vs.85).Aspx 468 | ;What Are Win Events: 469 | ; Https://Msdn.Microsoft.Com/En-Us/Library/Windows/Desktop/Dd373868(V=Vs.85).Aspx 470 | ;System-Level And Object-Level Events: 471 | ; Https://Msdn.Microsoft.Com/En-Us/Library/Windows/Desktop/Dd373657(V=Vs.85).Aspx 472 | ;Console Accessibility: 473 | ; Https://Msdn.Microsoft.Com/En-Us/Library/Ms971319.Aspx 474 | Static Event := { 475 | Min:0x00000001, 476 | Max:0x7fffffff, 477 | System_Sound:0x0001, 478 | System_Alert:0x0002, 479 | System_Foreground:0x0003, 480 | System_MenuStart:0x0004, 481 | System_MenuEnd:0x0005, 482 | System_MenuPopupStart:0x0006, 483 | System_MenuPopupEnd:0x0007, 484 | System_CaptureStart:0x0008, 485 | System_CaptureEnd:0x0009, 486 | System_MoveSizeStart:0x000a, 487 | System_MoveSizeEnd:0x000b, 488 | System_ContextHelpStart:0x000c, 489 | System_ContextHelpEnd:0x000d, 490 | System_DragDropStart:0x000e, 491 | System_DragDropEnd:0x000f, 492 | System_DialogStart:0x0010, 493 | System_DialogEnd:0x0011, 494 | System_ScrollingStart:0x0012, 495 | System_ScrollingEnd:0x0013, 496 | System_SwitchStart:0x0014, 497 | System_SwitchEnd:0x0015, 498 | System_MinimizeStart:0x0016, 499 | System_MinimizeEnd:0x0017, 500 | Console_Caret:0x4001, 501 | Console_Update_Region:0x4002, 502 | Console_Update_Simple:0x4003, 503 | Console_Update_Scroll:0x4004, 504 | Console_Layout:0x4005, 505 | Console_Start_Application:0x4006, 506 | Console_End_Application:0x4007, 507 | Object_Create:0x8000, 508 | Object_Destroy:0x8001, 509 | Object_Show:0x8002, 510 | Object_Hide:0x8003, 511 | Object_Reorder:0x8004, 512 | Object_Focus:0x8005, 513 | Object_Selection:0x8006, 514 | Object_SelectionAdd:0x8007, 515 | Object_SelectionRemove:0x8008, 516 | Object_SelectionWithin:0x8009, 517 | Object_StateChange:0x800a, 518 | Object_LocationChange:0x800b, 519 | Object_NameChange:0x800c, 520 | Object_DescriptionChange:0x800d, 521 | Object_ValueChange:0x800e, 522 | Object_ParentChange:0x800f, 523 | Object_HelpChange:0x8010, 524 | Object_DefactionChange:0x8011, 525 | Object_AcceleratorChange:0x8012, 526 | Object_Invoked:0x8013, 527 | Object_TextSelectionChanged:0x8014, 528 | Object_ContentScrolled:0x8015, 529 | System_ArrangmentPreview:0x8016, 530 | Object_Cloaked:0x8017, 531 | Object_Uncloaked:0x8018, 532 | Object_LiveRegionChanged:0x8019, 533 | Object_HostedObjectsInvalidated:0x8020, 534 | Object_DragStart:0x8021, 535 | Object_DragCancel:0x8022, 536 | Object_DragComplete:0x8023, 537 | Object_DragEnter:0x8024, 538 | Object_DragLeave:0x8025, 539 | Object_DragDropped:0x8026, 540 | Object_IME_Show:0x8027, 541 | Object_IME_Hide:0x8028, 542 | Object_IME_Change:0x8029, 543 | Object_TextEdit_ConversionTargetChanged:0x8030, 544 | Object_End:0x80FF, 545 | base:this.Enumeration.Prototype 546 | } 547 | 548 | Static WinEvent := { 549 | OutOfContext:0, 550 | SkipOwnThread:1, 551 | SkipOwnProcess:2, 552 | InContext:3, 553 | base:this.Enumeration.Prototype 554 | } 555 | 556 | class TypeValidation { 557 | static Element(arg) { 558 | if !arg || (arg is Acc.IAccessible) 559 | return arg 560 | throw TypeError("Element argument requires parameter with type Acc.JavaAccessibleContext, but received " Type(arg), -2) 561 | } 562 | static Integer(arg, paramName) { 563 | if IsInteger(arg) 564 | return Integer(arg) 565 | throw TypeError(paramName " requires type Integer, but received type " Type(arg), -2) 566 | } 567 | static String(arg, paramName) { 568 | if arg is String 569 | return arg 570 | if !(arg is Object) 571 | return String(arg) 572 | throw TypeError(paramName " requires type String, but received type " Type(arg), -2) 573 | } 574 | static Object(arg, paramName) { 575 | if arg is Object 576 | return arg 577 | throw TypeError(paramName " requires type Object, but received type " Type(arg), -2) 578 | } 579 | static TreeScope(arg) { 580 | if IsInteger(arg) { 581 | if arg < 1 || arg > 31 582 | throw ValueError("Acc.TreeScope does not contain constant `"" arg "`"", -2) 583 | return Integer(arg) 584 | } else if arg is String { 585 | try return Acc.TreeScope.%arg% 586 | throw ValueError("Acc.TreeScope does not contain value for `"" arg "`"", -2) 587 | } 588 | throw TypeError("Acc.TreeScope requires parameter with type Integer or String, but received " Type(arg), -2) 589 | } 590 | static TreeTraversalOptions(arg) { 591 | if IsInteger(arg) { 592 | return Integer(arg) 593 | } else if arg is String { 594 | try return Acc.TreeTraversalOptions.%arg% 595 | try return Acc.TreeTraversalOptions.%arg "Order"% 596 | throw ValueError("Acc.TreeTraversalOptions does not contain value for `"" arg "`"", -2) 597 | } 598 | throw TypeError("Invalid type provided for Acc.TreeTraversalOptions", -2, "Allowed types are Integer and String, but was provided type " Type(arg)) 599 | } 600 | } 601 | 602 | static __HighlightGuis := Map() 603 | static __ExtractNamedParameters(obj, params*) { 604 | local i := 0 605 | if !IsObject(obj) || Type(obj) != "Object" 606 | return 0 607 | Loop params.Length // 2 { 608 | name := params[++i], value := params[++i] 609 | if obj.HasProp(name) 610 | %value% := obj.%name%, obj.DeleteProp(name) 611 | } 612 | return 1 613 | } 614 | 615 | class IAccessible { 616 | /** 617 | * Internal method. Creates an Acc element from a raw IAccessible COM object and/or childId, 618 | * and additionally stores the hWnd of the window the object belongs to. 619 | * @param accessible IAccessible COM object 620 | * @param childId IAccessible childId 621 | * @param wId hWnd of the parent window 622 | */ 623 | __New(accessible, childId:=0, wId:=0) { 624 | if ComObjType(accessible, "Name") != "IAccessible" 625 | throw Error("Could not access an IAccessible Object") 626 | this.DefineProp("ptr", {value:ComObjValue(accessible)}) 627 | this.DefineProp("accessible", {value:accessible}) 628 | this.DefineProp("childId", {value:childId}) 629 | if wId 630 | this.DefineProp("WinID", {value:wId}) 631 | } 632 | __Delete() { 633 | if Acc.__HighlightGuis.Has(this) { 634 | for _, r in Acc.__HighlightGuis[this] 635 | r.Destroy() 636 | Acc.__HighlightGuis[this] := [] 637 | } 638 | } 639 | /** 640 | * Internal method. Is a wrapper to access acc properties or methods that take only the 641 | * childId as an input value. This usually shouldn't be called, unless the user is 642 | * trying to access a property/method that is undefined in this library. 643 | */ 644 | __Get(Name, Params) { 645 | if !(SubStr(Name,3)="acc") { 646 | try return this.accessible.acc%Name%[this.childId] 647 | try return this.accessible.acc%Name%(this.childId) ; try method with self 648 | try return this.accessible.acc%Name% ; try property 649 | } 650 | try return this.accessible.%Name%[this.childId] 651 | try return this.accessible.%Name%(this.childId) 652 | return this.accessible.%Name% 653 | } 654 | /** 655 | * Enables array-like use of Acc elements to access child elements. 656 | * If value is an integer then the nth corresponding child will be returned. 657 | * If value is a string, then it will be parsed as a comma-separated path which 658 | * allows both indexes (nth child) and RoleText values. 659 | * If value is an object, then it will be used in a FindElement call with scope set to Children. 660 | * @returns {Acc.IAccessible} 661 | */ 662 | __Item[params*] { 663 | get { 664 | oAcc := this 665 | for _, param in params { 666 | if IsInteger(param) 667 | oAcc := oAcc.GetNthChild(param) 668 | else if IsObject(param) 669 | oAcc := oAcc.FindElement(param, 2) 670 | else if Type(param) = "String" 671 | oAcc := Acc.ElementFromPath(param, oAcc, False) 672 | else 673 | TypeError("Invalid item type!", -1) 674 | } 675 | return oAcc 676 | } 677 | } 678 | /** 679 | * Enables enumeration of Acc elements, usually in a for loop. 680 | * Usage: 681 | * for [index, ] oChild in oElement 682 | */ 683 | __Enum(varCount) { 684 | maxLen := this.Length, i := 0, children := this.Children 685 | EnumElements(&element) { 686 | if ++i > maxLen 687 | return false 688 | element := children[i] 689 | return true 690 | } 691 | EnumIndexAndElements(&index, &element) { 692 | if ++i > maxLen 693 | return false 694 | index := i 695 | element := children[i] 696 | return true 697 | } 698 | return (varCount = 1) ? EnumElements : EnumIndexAndElements 699 | } 700 | /** 701 | * Internal method. Enables setting IAccessible acc properties. 702 | */ 703 | __Set(Name, Params, Value) { 704 | if !(SubStr(Name,3)="acc") 705 | try return this.accessible.acc%Name%[Params*] := Value 706 | return this.accessible.%Name%[Params*] := Value 707 | } 708 | /** 709 | * Internal method. Enables setting IAccessible acc properties. 710 | */ 711 | __Call(Name, Params) { 712 | if !(SubStr(Name,3)="acc") 713 | try return this.accessible.acc%Name%(Params.Length?Params[1]:0) 714 | return this.accessible.%Name%(Params*) 715 | } 716 | 717 | ; Wrappers for native IAccessible methods and properties. 718 | 719 | /** 720 | * Modifies the selection or moves the keyboard focus of the specified object. 721 | * Objects that support selection or receive the keyboard focus should support this method. 722 | * @param flags One of the SelectionFlag constants 723 | */ 724 | Select(flags) => (this.accessible.accSelect(IsInteger(flags) ? flags : Acc.SelectionFlag.%flags%,this.childId)) 725 | /** 726 | * Performs the specified object's default action. Not all objects have a default action. 727 | */ 728 | DoDefaultAction() => (this.accessible.accDoDefaultAction(this.childId)) 729 | /** 730 | * Retrieves the child element or child object that is displayed at a specific point on the screen. 731 | * This method usually shouldn't be called. To get the accessible object that is displayed at a point, 732 | * use the ElementFromPoint method, which calls this method internally on native IAccessible side. 733 | */ 734 | HitTest(x, y) => (this.IAccessibleFromVariant(this.accessible.accHitTest(x, y))) 735 | /** 736 | * Traverses to another UI element within a container and retrieves the object. 737 | * This method is deprecated and should not be used. 738 | * @param navDir One of the NavDir constants. 739 | * @returns {Acc.IAccessible} 740 | */ 741 | Navigate(navDir) { 742 | navDir := IsInteger(navDir) ? navDir : Acc.NavDir.%navDir% 743 | varEndUpAt := this.accessible.accNavigate(navDir,this.childId) 744 | if Type(varEndUpAt) = "ComObject" 745 | return Acc.IAccessible(Acc.Query(varEndUpAt)) 746 | else if IsInteger(varEndUpAt) 747 | return Acc.IAccessible(this.accessible, varEndUpAt) 748 | else 749 | return 750 | } 751 | Name { 752 | get => (this.accessible.accName[this.childId]) 753 | set => (this.accessible.accName[this.childId] := Value) 754 | } 755 | Value { 756 | get => (this.accessible.accValue[this.childId]) 757 | set => (this.accessible.accValue[this.childId] := Value) 758 | } 759 | Role => (this.accessible.accRole[this.childId]) ; Returns an integer 760 | RoleText => (Acc.GetRoleText(this.Role)) ; Returns a string 761 | Help => (this.accessible.accHelp[this.childId]) 762 | KeyboardShortcut => (this.accessible.accKeyboardShortcut[this.childId]) 763 | State => (this.accessible.accState[this.childId]) ; Returns an integer 764 | StateText => (Acc.GetStateText(this.accessible.accState[this.childId])) ; Returns a string 765 | Description => (this.accessible.accDescription[this.childId]) ; Returns a string 766 | DefaultAction => (this.accessible.accDefaultAction[this.childId]) ; Returns a string 767 | ; Retrieves the Acc element child that has the keyboard focus. 768 | Focus => (this.IAccessibleFromVariant(this.accessible.accFocus())) 769 | ; Returns an array of Acc elements that are the selected children of this object. 770 | Selection => (this.IAccessibleFromVariant(this.accessible.accSelection())) 771 | ; Returns the parent of this object as an Acc element 772 | Parent => (this.IsChild ? Acc.IAccessible(this.accessible,,this.WinID) : Acc.IAccessible(Acc.Query(this.accessible.accParent))) 773 | 774 | ; Returns the Hwnd for the control corresponding to this object 775 | ControlID { 776 | get { 777 | if DllCall("oleacc\WindowFromAccessibleObject", "Ptr", this, "uint*", &hWnd:=0) = 0 778 | return (this.DefineProp("ControlID", {value:hWnd}), hWnd) 779 | throw Error("WindowFromAccessibleObject failed", -1) 780 | } 781 | } 782 | ; Returns the Hwnd for the window corresponding to this object 783 | WinID { 784 | get { 785 | if DllCall("oleacc\WindowFromAccessibleObject", "Ptr", this, "uint*", &hWnd:=0) = 0 786 | return (hWnd := DllCall("GetAncestor", "Ptr", hWnd, "UInt", GA_ROOT := 2), this.DefineProp("WinID", {value:hWnd}), hWnd) 787 | throw Error("WindowFromAccessibleObject failed", -1) 788 | } 789 | } 790 | ; Checks whether internally this corresponds to a native IAccessible object or a childId 791 | IsChild => (this.DefineProp("IsChild", {value:(this.childId == 0 ? False : True)}), this.IsChild) 792 | ; Checks whether this object is selected. This is very slow, a better alternative is Element.Selection 793 | IsSelected { 794 | get { 795 | try oSel := this.Parent.Selection 796 | return IsSet(oSel) && this.IsEqual(oSel) 797 | } 798 | } 799 | ; Returns the child count of this object 800 | Length => (this.childId == 0 ? this.accessible.accChildCount : 0) 801 | ; Checks whether this object still exists and is visible/accessible 802 | Exists { 803 | get { 804 | try { 805 | if ((state := this.State) == 32768) || (state == 1) || (((pos := this.Location).x==0) && (pos.y==0) && (pos.w==0) && (pos.h==0)) 806 | return 0 807 | } catch 808 | return 0 809 | return 1 810 | } 811 | } 812 | /** 813 | * Returns an object containing the location of this element 814 | * @returns {Object} {x: screen x-coordinate, y: screen y-coordinate, w: width, h: height} 815 | */ 816 | Location { 817 | get { 818 | x:=Buffer(4, 0), y:=Buffer(4, 0), w:=Buffer(4, 0), h:=Buffer(4, 0) 819 | this.accessible.accLocation(ComValue(0x4003, x.ptr, 1), ComValue(0x4003, y.ptr, 1), ComValue(0x4003, w.ptr, 1), ComValue(0x4003, h.ptr, 1), this.childId) 820 | Return {x:NumGet(x,0,"int"), y:NumGet(y,0,"int"), w:NumGet(w,0,"int"), h:NumGet(h,0,"int")} 821 | } 822 | } 823 | ; Returns all children of this object as an array of Acc elements 824 | Children { 825 | get { 826 | if this.IsChild || !(cChildren := this.accessible.accChildCount) 827 | return [] 828 | Children := Array(), varChildren := Buffer(cChildren * (8+2*A_PtrSize)) 829 | try { 830 | if DllCall("oleacc\AccessibleChildren", "ptr", this, "int",0, "int", cChildren, "ptr", varChildren, "int*", cChildren) > -1 { 831 | Loop cChildren { 832 | i := (A_Index-1) * (A_PtrSize * 2 + 8) + 8 833 | child := NumGet(varChildren, i, "ptr") 834 | Children.Push(NumGet(varChildren, i-8, "ptr") = 9 ? Acc.IAccessible(Acc.Query(child),,this.WinID) : Acc.IAccessible(this.accessible, child, this.WinID)) 835 | NumGet(varChildren, i-8, "ptr") = 9 ? ObjRelease(child) : "" 836 | } 837 | Return Children 838 | } 839 | } 840 | throw Error("AccessibleChildren DllCall Failed", -1) 841 | } 842 | } 843 | ReversedChildren => this.Children.DefineProp("__Enum", {call: (s, i) => (i = 1 ? (i := s.Length, (&v) => i > 0 ? (v := s[i], i--) : false) : (i := s.Length, (&k, &v) => (i > 0 ? (k := i, v := s[i], i--) : false)))}) 844 | 845 | Identity { 846 | get { 847 | pIAccIdentity := ComObjQuery(this.accessible, "{7852B78D-1CFD-41C1-A615-9C0C85960B5F}") 848 | if ComCall(3, pIAccIdentity, "int", this.childId, "ptr*", &ppIDString:=0, "int*", &pdwIDStringLen := 0) = 0 { ; GetIdentityString 849 | byteBuffer := Buffer(pdwIDStringLen) 850 | result := "" 851 | Loop pdwIDStringLen 852 | result .= Format("{:x}", NumGet(ppIDString, A_Index-1, "uchar")) 853 | if ppIDString 854 | DllCall("ole32.dll\CoTaskMemFree", "ptr", ppIDString) 855 | return result 856 | } else 857 | throw Error("GetIdentityString call failed! Property probably not implemented.", -1) 858 | } 859 | } 860 | /** 861 | * Internal method. Used to convert a variant returned by native IAccessible to 862 | * an Acc element or an array of Acc elements. 863 | */ 864 | IAccessibleFromVariant(var) { 865 | if Type(var) = "ComObject" 866 | return Acc.IAccessible(Acc.Query(var),,this.WinID) 867 | else if Type(var) = "Enumerator" { 868 | oArr := [] 869 | Loop { 870 | if var.Call(&childId) 871 | oArr.Push(this.IAccessibleFromVariant(childId)) 872 | else 873 | return oArr 874 | } 875 | } else if IsInteger(var) 876 | return Acc.IAccessible(this.accessible,var,this.WinID) 877 | else 878 | return var 879 | } 880 | ; Returns the nth child of this element. Equivalent to Element[n] 881 | GetNthChild(n) { 882 | if !IsNumber(n) 883 | throw TypeError("Child must be an integer", -1) 884 | n := Integer(n) 885 | cChildren := this.accessible.accChildCount 886 | if n > cChildren 887 | throw IndexError("Child index " n " is out of bounds", -1) 888 | varChildren := Buffer(cChildren * (8+2*A_PtrSize)) 889 | try { 890 | if DllCall("oleacc\AccessibleChildren", "ptr", this, "int",0, "int",cChildren, "ptr",varChildren, "int*",cChildren) > -1 { 891 | if n < 1 892 | n := cChildren + n + 1 893 | if n < 1 || n > cChildren 894 | throw IndexError("Child index " n " is out of bounds", -1) 895 | i := (n-1) * (A_PtrSize * 2 + 8) + 8 896 | child := NumGet(varChildren, i, "ptr") 897 | oChild := NumGet(varChildren, i-8, "ptr") = 9 ? Acc.IAccessible(Acc.Query(child),,this.WinID) : Acc.IAccessible(this.accessible, child, this.WinID) 898 | NumGet(varChildren, i-8, "ptr") = 9 ? ObjRelease(child) : "" 899 | Return oChild 900 | } 901 | } 902 | throw Error("AccessibleChildren DllCall Failed", -1) 903 | } 904 | 905 | /** 906 | * Returns the path from the current element to oTarget element. 907 | * The returned path is a comma-separated list of integers corresponding to the order the 908 | * IAccessible tree needs to be traversed to access oTarget element from this element. 909 | * If no path is found then an empty string is returned. 910 | * @param oTarget An Acc element. 911 | */ 912 | GetPath(oTarget) { 913 | if Type(oTarget) != "Acc.IAccessible" 914 | throw TypeError("oTarget must be a valid Acc element!", -1) 915 | oNext := oTarget, oPrev := oTarget, path := "" 916 | try { 917 | while !this.IsEqual(oNext) 918 | for i, oChild in oNext := oNext.Parent { 919 | if oChild.IsEqual(oPrev) { 920 | path := i "," path, oPrev := oNext 921 | break 922 | } 923 | } 924 | path := SubStr(path, 1, -1) 925 | if Acc.ElementFromPath(path, this, False).IsEqual(oTarget) 926 | return path 927 | } 928 | oFind := this.FindElement({IsEqual:oTarget}) 929 | return oFind ? oFind.Path : "" 930 | } 931 | /** 932 | * Returns an object containing the x, y coordinates and width and height: {x:x coordinate, y:y coordinate, w:width, h:height}. 933 | * @param relativeTo Coordinate mode, which can be client, window or screen. Default is A_CoordModeMouse. 934 | * @returns {Object} {x: relative x-coordinate, y: relative y-coordinate, w: width, h: height} 935 | */ 936 | GetLocation(relativeTo:="") { 937 | relativeTo := (relativeTo == "") ? A_CoordModeMouse : relativeTo, loc := this.Location 938 | if (relativeTo = "screen") 939 | return loc 940 | else if (relativeTo = "window") { 941 | RECT := Buffer(16) 942 | DllCall("user32\GetWindowRect", "Int", this.WinID, "Ptr", RECT) 943 | return {x:(loc.x-NumGet(RECT, 0, "Int")), y:(loc.y-NumGet(RECT, 4, "Int")), w:loc.w, h:loc.h} 944 | } else if (relativeTo = "client") { 945 | pt := Buffer(8), NumPut("int",loc.x,pt), NumPut("int",loc.y,pt,4) 946 | DllCall("ScreenToClient", "Int", this.WinID, "Ptr", pt) 947 | return {x:NumGet(pt,0,"int"), y:NumGet(pt,4,"int"), w:loc.w, h:loc.h} 948 | } else 949 | throw Error(relativeTo "is not a valid CoordMode",-1) 950 | } 951 | /** 952 | * Checks whether this element is equal to another element 953 | * @param oCompare The Acc element to be compared against. 954 | */ 955 | IsEqual(oCompare) { 956 | loc1 := {x:0,y:0,w:0,h:0}, loc2 := {x:0,y:0,w:0,h:0} 957 | try loc1 := this.Location 958 | catch { ; loc1 unset 959 | loc1 := {x:0,y:0,w:0,h:0} 960 | try return oCompare.Location && 0 ; if loc2 is set then code will return 961 | } 962 | try loc2 := oCompare.Location 963 | if (loc1.x != loc2.x) || (loc1.y != loc2.y) || (loc1.w != loc2.w) || (loc1.h != loc2.h) 964 | return 0 965 | for _, v in ((loc1.x = 0) && (loc1.y = 0) && (loc1.w = 0) && (loc1.h = 0)) ? ["Role", "Value", "Name", "State", "DefaultAction", "Description", "KeyboardShortcut", "Help"] : ["Role", "Name"] { 966 | try v1 := this.%v% 967 | catch { ; v1 unset 968 | try v2 := oCompare.%v% 969 | catch ; both unset, continue 970 | continue 971 | return 0 ; v1 unset, v2 set 972 | } 973 | try v2 := oCompare.%v% 974 | catch ; v1 set, v2 unset 975 | return 0 976 | if v1 != v2 ; both set 977 | return 0 978 | } 979 | return 1 980 | } 981 | /** 982 | * Finds the first element matching a set of conditions. 983 | * The returned element also has a "Path" property with the found elements path 984 | * @param condition Condition object (see ValidateCondition) 985 | * @param scope The search scope (Acc.TreeScope value): Element, Children, Family (Element+Children), Descendants, SubTree (Element+Descendants). Default is Descendants. 986 | * @param index Looks for the n-th element matching the condition 987 | * @param order The order of tree traversal (Acc.TreeTraversalOptions value): Default, LastToFirst, PostOrder. Default is FirstToLast and PreOrder. 988 | * @param maxDepth Maximum level of depth for the search. Default is no limit. 989 | * @returns {Acc.IAccessible} 990 | */ 991 | FindElement(condition, scope:=4, index:=1, order:=0, maxDepth:=Acc.MaxRecurseDepth) { 992 | local callback := 0, found, c := 0 993 | Acc.__ExtractNamedParameters(condition, "scope", &scope, "index", &index, "i", &index, "order", &order, "maxDepth", &maxDepth, "callback", &callback, "condition", &condition) 994 | callback := callback || Acc.IAccessible.Prototype.ValidateCondition.Bind(unset, condition), scope := Acc.TypeValidation.TreeScope(scope), index := Acc.TypeValidation.Integer(index, "Index"), order := Acc.TypeValidation.TreeTraversalOptions(order) 995 | if index < 0 996 | order |= 2, index := -index 997 | else if index = 0 998 | throw ValueError("Condition index cannot be 0", -1) 999 | ChildOrder := order&2 ? "ReversedChildren" : "Children" 1000 | if order&1 { 1001 | if IsObject(found := PostOrderRecursiveFind(this, scope)) 1002 | return found 1003 | throw TargetError("An element matching the condition was not found", -1) 1004 | } 1005 | if scope&1 && (c := this.ValidateCondition(condition, 1, 0)) > 0 && (--index = 0) 1006 | return this.DefineProp("Path", {value:""}) 1007 | if scope>1 && c >= 0 && found := PreOrderRecursiveFind(this) 1008 | return found 1009 | throw TargetError("An element matching the condition was not found", -1) 1010 | 1011 | PreOrderRecursiveFind(element, path:="", depth:=0) { 1012 | local c 1013 | if (++depth, depth > maxDepth) 1014 | return 1015 | for i, child in element.%ChildOrder% { 1016 | if (c := 0, c := child.ValidateCondition(condition, i, depth)) > 0 && (--index = 0) 1017 | return child.DefineProp("Path", {value:Trim(path "," i, ",")}) 1018 | else if (c >= 0) && (scope&4) && (rf := PreOrderRecursiveFind(child, path "," i, depth)) 1019 | return rf 1020 | } 1021 | } 1022 | PostOrderRecursiveFind(element, scope, path:="", i:=1, depth:=-1) { 1023 | if (++depth, depth > maxDepth) 1024 | return 1025 | local c := 0 1026 | if scope>1 { 1027 | for j, child in element.%ChildOrder% { 1028 | if IsObject(rf := PostOrderRecursiveFind(child, (scope & ~2)|1, path "," j, j, depth)) 1029 | return rf 1030 | else if rf = -1 1031 | break 1032 | } 1033 | } 1034 | if scope&1 && (c := element.ValidateCondition(condition, i, depth)) > 0 && (--index = 0) 1035 | return element.DefineProp("Path", {value:Trim(path, ",")}) 1036 | return c 1037 | } 1038 | } 1039 | FindFirst(args*) => this.FindElement(args*) 1040 | ElementExist(condition, scope:=4, index:=1, order:=0, maxDepth:=0xFFFFFFFF) { 1041 | try return this.FindElement(condition, scope, index, order, maxDepth) 1042 | return 0 1043 | } 1044 | /** 1045 | * Returns an array of elements matching the condition (see description under ValidateCondition) 1046 | * The returned elements also have the "Path" property with the found elements path 1047 | * @param condition Condition object (see ValidateCondition). Default is to match any condition. 1048 | * @param scope The search scope (Acc.TreeScope value): Element, Children, Family (Element+Children), Descendants, SubTree (Element+Descendants). Default is Descendants. 1049 | * @param order The order of tree traversal (Acc.TreeTraversalOptions value): Default, LastToFirst, PostOrder. Default is FirstToLast and PreOrder. 1050 | * @param maxDepth Maximum level of depth for the search. Default is no limit. 1051 | * @returns {[Acc.IAccessible]} 1052 | */ 1053 | FindElements(condition:=True, scope:=4, order:=0, maxDepth:=Acc.MaxRecurseDepth) { 1054 | local callback := 0, foundElements := [], c := 0 1055 | Acc.__ExtractNamedParameters(condition, "scope", &scope, "order", &order, "maxDepth", &maxDepth, "callback", &callback, "condition", &condition) 1056 | callback := callback || Acc.IAccessible.Prototype.ValidateCondition.Bind(unset, condition), scope := Acc.TypeValidation.TreeScope(scope), order := Acc.TypeValidation.TreeTraversalOptions(order) 1057 | ChildOrder := order&2 ? "ReversedChildren" : "Children" 1058 | ; First handle PostOrder 1059 | if order&1 1060 | return (PostOrderRecursiveFind(this, scope), foundElements) 1061 | ; PreOrder 1062 | if scope&1 && (c := callback(this, 1, 0)) > 0 1063 | this.DefineProp("Path", {value:""}), foundElements.Push(this) 1064 | if scope > 1 && c >= 0 1065 | return (PreOrderRecursiveFind(this), foundElements) 1066 | return foundElements 1067 | 1068 | PreOrderRecursiveFind(el, path:="", depth:=0) { 1069 | local child, c 1070 | if (++depth, depth > maxDepth) 1071 | return 1072 | for i, child in el.%ChildOrder% { 1073 | c := 0 1074 | if (c := callback(child, i, depth)) > 0 1075 | child.DefineProp("Path", {value:Trim(path "," i, ",")}), foundElements.Push(child) 1076 | if c >= 0 && scope&4 1077 | PreOrderRecursiveFind(child, path "," i, depth) 1078 | } 1079 | } 1080 | PostOrderRecursiveFind(el, scope, path:="", i:=1, depth:=-1) { 1081 | local child, c := 0 1082 | if (++depth, depth > maxDepth) 1083 | return c 1084 | if scope > 1 { 1085 | for j, child in el.%ChildOrder% { 1086 | if PostOrderRecursiveFind(child, (scope & ~2)|1, path "," j, j, depth) = -1 1087 | break 1088 | } 1089 | } 1090 | if scope&1 && (c := callback(el, i, depth)) > 0 1091 | el.DefineProp("Path", {value:Trim(path, ",")}), foundElements.Push(el) 1092 | return c 1093 | } 1094 | } 1095 | FindAll(args*) => this.FindElements(args*) 1096 | /** 1097 | * Waits for an element matching a condition or path to exist in the Acc tree. 1098 | * Element being in the Acc tree doesn't mean it's necessarily visible or interactable, 1099 | * use WaitElementExist for that. 1100 | * @param conditionOrPath Condition object (see ValidateCondition), or Acc path as a string (comma-separated numbers) 1101 | * @param timeOut Timeout in milliseconds. Default in indefinite waiting. 1102 | * @param scope The search scope (Acc.TreeScope value): Element, Children, Family (Element+Children), Descendants, SubTree (Element+Descendants). Default is Descendants. 1103 | * @param index Looks for the n-th element matching the condition 1104 | * @param order The order of tree traversal (Acc.TreeTraversalOptions value): Default, PostOrder, LastToFirst. Default is FirstToLast and PreOrder. 1105 | * @param depth Maximum level of depth for the search. Default is no limit. 1106 | * @returns {Acc.IAccessible} 1107 | */ 1108 | WaitElement(conditionOrPath, timeOut:=-1, scope:=4, index:=1, order:=0, depth:=-1) { 1109 | if Type(conditionOrPath) = "Object" && conditionOrPath.HasOwnProp("timeOut") 1110 | timeOut := conditionOrPath.timeOut 1111 | waitTime := A_TickCount + timeOut 1112 | while ((timeOut < 1) ? 1 : (A_tickCount < waitTime)) { 1113 | try return IsObject(conditionOrPath) ? this.FindElement(conditionOrPath, scope, index, depth) : this[conditionOrPath] 1114 | Sleep 40 1115 | } 1116 | } 1117 | /** 1118 | * Waits for an element matching a condition or path to appear. 1119 | * @param conditionOrPath Condition object (see ValidateCondition), or Acc path as a string (comma-separated numbers) 1120 | * @param timeOut Timeout in milliseconds. Default in indefinite waiting. 1121 | * @param scope The search scope (Acc.TreeScope value): Element, Children, Family (Element+Children), Descendants, SubTree (Element+Descendants). Default is Descendants. 1122 | * @param index Looks for the n-th element matching the condition 1123 | * @param order The order of tree traversal (Acc.TreeTraversalOptions value): Default, LastToFirst, PostOrder. Default is FirstToLast and PreOrder. 1124 | * @param depth Maximum level of depth for the search. Default is no limit. 1125 | * @returns {Acc.IAccessible} 1126 | */ 1127 | WaitElementExist(conditionOrPath, timeOut:=-1, scope:=4, index:=1, order:=0, depth:=-1) { 1128 | if Type(conditionOrPath) = "Object" && conditionOrPath.HasOwnProp("timeOut") 1129 | timeOut := conditionOrPath.timeOut 1130 | waitTime := A_TickCount + timeOut 1131 | while ((timeOut < 1) ? 1 : (A_tickCount < waitTime)) { 1132 | try { 1133 | oFind := IsObject(conditionOrPath) ? this.FindElement(conditionOrPath, scope, index, depth) : this[conditionOrPath] 1134 | if oFind.Exists 1135 | return oFind 1136 | } 1137 | Sleep 40 1138 | } 1139 | } 1140 | /** 1141 | * Waits for this element to not exist. Returns True if the element disappears before the timeout. 1142 | * @param timeOut Timeout in milliseconds. Default in indefinite waiting. 1143 | */ 1144 | WaitNotExist(timeOut:=-1) { 1145 | waitTime := A_TickCount + timeOut 1146 | while ((timeOut < 1) ? 1 : (A_tickCount < waitTime)) { 1147 | if !this.Exists 1148 | return 1 1149 | Sleep 40 1150 | } 1151 | } 1152 | /** 1153 | * Checks whether the current element or any of its ancestors match the condition, 1154 | * and returns that Acc element. If no element is found, an error is thrown. 1155 | * @param condition Condition object (see ValidateCondition) 1156 | * @returns {Acc.IAccessible} 1157 | */ 1158 | Normalize(condition) { 1159 | if this.ValidateCondition(condition) 1160 | return this 1161 | oEl := this 1162 | Loop { 1163 | try { 1164 | oEl := oEl.Parent 1165 | if oEl.ValidateCondition(condition) 1166 | return oEl 1167 | } catch 1168 | break 1169 | } 1170 | return 0 1171 | } 1172 | 1173 | /* 1174 | Checks whether the element matches a provided condition. 1175 | Everything inside {} is an "and" condition 1176 | Everything inside [] is an "or" condition 1177 | Object key "not" creates a not condition 1178 | 1179 | matchmode key defines the MatchMode: StartsWith, Substring, Exact, RegEx (Acc.MATCHMODE values) 1180 | 1181 | casesensitive key defines case sensitivity: True=case sensitive; False=case insensitive 1182 | 1183 | {Name:"Something"} => Name must match "Something" (case sensitive) 1184 | {Name:"Something", RoleText:"something else"} => Name must match "Something" and RoleText must match "something else" 1185 | [{Name:"Something", Role:42}, {Name:"Something2", RoleText:"something else"}] => Name=="Something" and Role==42 OR Name=="Something2" and RoleText=="something else" 1186 | {Name:"Something", not:[RoleText:"something", RoleText:"something else"]} => Name must match "something" and RoleText cannot match "something" nor "something else" 1187 | */ 1188 | ValidateCondition(oCond, i?, depth?) { 1189 | if !IsObject(oCond) 1190 | return !!oCond ; if oCond is not an object, then it is treated as True or False condition 1191 | if HasMethod(oCond) 1192 | return oCond(this, i?, depth?) 1193 | else if oCond is Array { ; or condition 1194 | for _, c in oCond 1195 | if this.ValidateCondition(c, i?, depth?) 1196 | return 1 1197 | return 0 1198 | } 1199 | matchmode := 3, casesensitive := 1, notCond := False 1200 | for p in ["matchmode", "mm"] 1201 | if oCond.HasOwnProp(p) 1202 | matchmode := oCond.%p% 1203 | try matchmode := IsInteger(matchmode) ? matchmode : Acc.MATCHMODE.%matchmode% 1204 | for p in ["casesensitive", "cs"] 1205 | if oCond.HasOwnProp(p) 1206 | casesensitive := oCond.%p% 1207 | for prop, cond in oCond.OwnProps() { 1208 | switch Type(cond) { ; and condition 1209 | case "String", "Integer": 1210 | if prop ~= "i)^(index|i|matchmode|mm|casesensitive|cs|scope|timeout)$" 1211 | continue 1212 | propValue := "" 1213 | try propValue := this.%prop% 1214 | switch matchmode, 0 { 1215 | case 2: 1216 | if !InStr(propValue, cond, casesensitive) 1217 | return 0 1218 | case 1: 1219 | if !((casesensitive && (SubStr(propValue, 1, StrLen(cond)) == cond)) || (!casesensitive && (SubStr(propValue, 1, StrLen(cond)) = cond))) 1220 | return 0 1221 | case "Regex": 1222 | if !(propValue ~= cond) 1223 | return 0 1224 | default: 1225 | if !((casesensitive && (propValue == cond)) || (!casesensitive && (propValue = cond))) 1226 | return 0 1227 | } 1228 | case "Acc.IAccessible": 1229 | if (prop="IsEqual") ? !this.IsEqual(cond) : !this.ValidateCondition(cond, i?, depth?) 1230 | return 0 1231 | default: 1232 | if (HasProp(cond, "Length") ? cond.Length = 0 : ObjOwnPropCount(cond) = 0) { 1233 | try return this.%prop% && 0 1234 | catch 1235 | return 1 1236 | } else if (prop = "Location") { 1237 | try loc := cond.HasOwnProp("relative") ? this.GetLocation(cond.relative) 1238 | : cond.HasOwnProp("r") ? this.GetLocation(cond.r) 1239 | : this.Location 1240 | catch 1241 | return 0 1242 | for lprop, lval in cond.OwnProps() { 1243 | if (!((lprop = "relative") || (lprop = "r")) && (loc.%lprop% != lval)) 1244 | return 0 1245 | } 1246 | } else if ((prop = "not") ? this.ValidateCondition(cond, i?, depth?) : !this.ValidateCondition(cond, i?, depth?)) 1247 | return 0 1248 | } 1249 | } 1250 | return 1 1251 | } 1252 | /** 1253 | * Outputs relevant information about the element 1254 | * @param scope The search scope: Element, Children, Family (Element+Children), Descendants, SubTree (Element+Descendants). Default is Element. 1255 | * @param delimiter The delimiter separating the outputted properties 1256 | * @param maxDepth Maximum number of levels to dump. Default is no limit. 1257 | * @returns {String} 1258 | */ 1259 | Dump(scope:=1, delimiter:=" ", maxDepth:=Acc.MaxRecurseDepth) { 1260 | out := "", scope := IsInteger(scope) ? scope : Acc.TreeScope.%scope% 1261 | if scope&1 { 1262 | RoleText := "N/A", Role := "N/A", Value := "N/A", Name := "N/A", StateText := "N/A", State := "N/A", DefaultAction := "N/A", Description := "N/A", KeyboardShortcut := "N/A", Help := "N/A", Location := {x:"N/A",y:"N/A",w:"N/A",h:"N/A"} 1263 | for _, v in ["RoleText", "Role", "Value", "Name", "StateText", "State", "DefaultAction", "Description", "KeyboardShortcut", "Help", "Location"] 1264 | try %v% := this.%v% 1265 | out := "RoleText: " RoleText delimiter "Role: " Role delimiter "[Location: {x:" Location.x ",y:" Location.y ",w:" Location.w ",h:" Location.h "}]" delimiter "[Name: " Name "]" delimiter "[Value: " Value "]" (StateText ? delimiter "[StateText: " StateText "]" : "") (State ? delimiter "[State: " State "]" : "") (DefaultAction ? delimiter "[DefaultAction: " DefaultAction "]" : "") (Description ? delimiter "[Description: " Description "]" : "") (KeyboardShortcut ? delimiter "[KeyboardShortcut: " KeyboardShortcut "]" : "") (Help ? delimiter "[Help: " Help "]" : "") (this.childId ? delimiter "ChildId: " this.childId : "") "`n" 1266 | } 1267 | if scope&4 1268 | return Trim(RecurseTree(this, out), "`n") 1269 | if scope&2 { 1270 | for n, oChild in this.Children 1271 | out .= n ":" delimiter oChild.Dump() "`n" 1272 | } 1273 | return Trim(out, "`n") 1274 | 1275 | RecurseTree(oAcc, tree, path:="", depth:=0) { 1276 | if (++depth, depth > maxDepth) 1277 | return tree 1278 | try { 1279 | if !oAcc.Length 1280 | return tree 1281 | } catch 1282 | return tree 1283 | 1284 | For i, oChild in oAcc.Children { 1285 | tree .= path (path?",":"") i ":" delimiter oChild.Dump() "`n" 1286 | tree := RecurseTree(oChild, tree, path (path?",":"") i, depth) 1287 | } 1288 | return tree 1289 | } 1290 | } 1291 | /** 1292 | * Outputs relevant information about the element and all its descendants. 1293 | * @param delimiter The delimiter separating the outputted properties 1294 | * @param depth Maximum number of levels to dump. Default is no limit. 1295 | * @returns {String} 1296 | */ 1297 | DumpAll(delimiter:=" ", maxDepth:=Acc.MaxRecurseDepth) => this.Dump(5, delimiter, maxDepth) 1298 | ; Same as Dump() 1299 | ToString() => this.Dump() 1300 | 1301 | /** 1302 | * Highlights the element for a chosen period of time. 1303 | * @param showTime Can be one of the following: 1304 | * Unset - highlights for 2 seconds, or removes the highlighting 1305 | * 0 - Indefinite highlighting. If the element object gets destroyed, so does the highlighting. 1306 | * Positive integer (eg 2000) - will highlight and pause for the specified amount of time in ms 1307 | * Negative integer - will highlight for the specified amount of time in ms, but script execution will continue 1308 | * "clear" - removes the highlight 1309 | * @param color The color of the highlighting. Default is red. 1310 | * @param d The border thickness of the highlighting in pixels. Default is 2. 1311 | * @returns {Acc.IAccessible} 1312 | */ 1313 | Highlight(showTime:=unset, color:="Red", d:=2) { 1314 | if !Acc.__HighlightGuis.Has(this) 1315 | Acc.__HighlightGuis[this] := [] 1316 | if (!IsSet(showTime) && Acc.__HighlightGuis[this].Length) || (IsSet(showTime) && showTime = "clear") { 1317 | for _, r in Acc.__HighlightGuis[this] 1318 | r.Destroy() 1319 | Acc.__HighlightGuis[this] := [] 1320 | return this 1321 | } else if !IsSet(showTime) 1322 | showTime := 2000 1323 | try loc := this.Location 1324 | if !IsSet(loc) || !IsObject(loc) 1325 | return this 1326 | Loop 4 { 1327 | Acc.__HighlightGuis[this].Push(Gui("+AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x08000000")) 1328 | } 1329 | Loop 4 1330 | { 1331 | i:=A_Index 1332 | , x1:=(i=2 ? loc.x+loc.w : loc.x-d) 1333 | , y1:=(i=3 ? loc.y+loc.h : loc.y-d) 1334 | , w1:=(i=1 or i=3 ? loc.w+2*d : d) 1335 | , h1:=(i=2 or i=4 ? loc.h+2*d : d) 1336 | Acc.__HighlightGuis[this][i].BackColor := color 1337 | Acc.__HighlightGuis[this][i].Show("NA x" . x1 . " y" . y1 . " w" . w1 . " h" . h1) 1338 | } 1339 | if showTime > 0 { 1340 | Sleep(showTime) 1341 | this.Highlight() 1342 | } else if showTime < 0 1343 | SetTimer(ObjBindMethod(this, "Highlight", "clear"), -Abs(showTime)) 1344 | return this 1345 | } 1346 | ClearHighlight() => this.Highlight("clear") 1347 | 1348 | /** 1349 | * Clicks the center of the element. 1350 | * @param WhichButton Left (default), Right, Middle (or just the first letter of each of these); or the fourth or fifth mouse button (X1 or X2). 1351 | * If WhichButton is an Integer, then Sleep will be called with that number. 1352 | * Example: Click(200) will sleep 200ms after clicking 1353 | * @param ClickCount The number of times to click the mouse. 1354 | * If ClickCount is a number >=10, then Sleep will be called with that number. 1355 | * To click 10+ times and sleep after, specify "ClickCount SleepTime". 1356 | * Example: Click("left", 200) will sleep 200ms after clicking. 1357 | * Example: Click("left", "20 200") will left-click 20 times and then sleep 200ms. 1358 | * @param DownOrUp This component is normally omitted, in which case each click consists of a down-event followed by an up-event. 1359 | * Otherwise, specify the word Down (or the letter D) to press the mouse button down without releasing it. 1360 | * Later, use the word Up (or the letter U) to release the mouse button. 1361 | * @param Relative Optional offset values for both X and Y (eg "-5 10" would offset X by -5 and Y by +10). 1362 | * @param NoActivate Setting NoActivate to True will prevent the window from being brought to front if the clickable point is not visible on screen. 1363 | * @returns {Acc.IAccessible} 1364 | */ 1365 | Click(WhichButton:="left", ClickCount:=1, DownOrUp:="", Relative:="", NoActivate:=False) { 1366 | rel := [0,0], pos := this.Location, saveCoordMode := A_CoordModeMouse, cCount := 1, SleepTime := -1 1367 | if (Relative && !InStr(Relative, "rel")) 1368 | rel := StrSplit(Relative, " "), Relative := "" 1369 | if IsInteger(WhichButton) 1370 | SleepTime := WhichButton, WhichButton := "left" 1371 | if !IsInteger(ClickCount) && InStr(ClickCount, " ") { 1372 | sCount := StrSplit(ClickCount, " ") 1373 | cCount := sCount[1], SleepTime := sCount[2] 1374 | } else if ClickCount > 9 { 1375 | SleepTime := cCount, cCount := 1 1376 | } 1377 | if (!NoActivate && (Acc.WindowFromPoint(pos.x+pos.w//2+rel[1], pos.y+pos.h//2+rel[2]) != this.WinID)) { 1378 | WinActivate(this.WinID) 1379 | WinWaitActive(this.WinID) 1380 | } 1381 | CoordMode("Mouse", "Screen") 1382 | Click(pos.x+pos.w//2+rel[1] " " pos.y+pos.h//2+rel[2] " " WhichButton (ClickCount ? " " ClickCount : "") (DownOrUp ? " " DownOrUp : "") (Relative ? " " Relative : "")) 1383 | CoordMode("Mouse", saveCoordMode) 1384 | Sleep(SleepTime) 1385 | return this 1386 | } 1387 | 1388 | /** 1389 | * ControlClicks the center of the element after getting relative coordinates with GetLocation("client"). 1390 | * @param WhichButton The button to click: LEFT, RIGHT, MIDDLE (or just the first letter of each of these). 1391 | * If omitted or blank, the LEFT button will be used. 1392 | * If an Integer is provided then a Sleep will be called afterwards. 1393 | * Ex: ControlClick(200) will sleep 200ms after clicking. 1394 | * @returns {Acc.IAccessible} 1395 | */ 1396 | ControlClick(WhichButton:="left", ClickCount:=1, Options:="") { 1397 | pos := this.GetLocation("client") 1398 | ControlClick("X" pos.x+pos.w//2 " Y" pos.y+pos.h//2, this.WinID,, IsInteger(WhichButton) ? "left" : WhichButton, ClickCount, Options) 1399 | if IsInteger(WhichButton) 1400 | Sleep(WhichButton) 1401 | return this 1402 | } 1403 | } 1404 | 1405 | /** 1406 | * Returns an Acc element from a screen coordinate. If both coordinates are omitted then 1407 | * the element under the mouse will be returned. 1408 | * @param x The x-coordinate 1409 | * @param y The y-coordinate 1410 | * @param activateChromium Whether to turn on accessibility for Chromium-based windows. Default is True. 1411 | * @returns {Acc.IAccessible} 1412 | */ 1413 | static ElementFromPoint(x:=unset, y:=unset, activateChromium:=True) { 1414 | if !(IsSet(x) && IsSet(y)) 1415 | DllCall("GetCursorPos", "int64P", &pt64:=0), x := 0xFFFFFFFF & pt64, y := pt64 >> 32 1416 | else 1417 | pt64 := y << 32 | (x & 0xFFFFFFFF) 1418 | wId := DllCall("GetAncestor", "Ptr", DllCall("user32.dll\WindowFromPoint", "int64", pt64), "UInt", 2) ; hwnd from point by SKAN. 2 = GA_ROOT 1419 | if activateChromium 1420 | Acc.ActivateChromiumAccessibility(wId) 1421 | pvarChild := Buffer(8 + 2 * A_PtrSize) 1422 | if DllCall("oleacc\AccessibleObjectFromPoint", "int64",pt64, "ptr*",&ppAcc := 0, "ptr",pvarChild) = 0 1423 | { ; returns a pointer from which we get a Com Object 1424 | return Acc.IAccessible(ComValue(9, ppAcc), NumGet(pvarChild,8,"UInt"), wId) 1425 | } 1426 | } 1427 | ; Wrapper for native function name 1428 | static ObjectFromPoint(args*) => Acc.ElementFromPoint(args*) 1429 | 1430 | /** 1431 | * Returns an Acc element corresponding to the provided window Hwnd. 1432 | * @param hWnd The window Hwnd. Default is Last Found Window. 1433 | * @param idObject An Acc.ObjId constant. Default is Acc.ObjId.Window (value 0). 1434 | * Note that to get objects by control Hwnds, use ObjId.Client (value -4). 1435 | * @param activateChromium Whether to turn on accessibility for Chromium-based windows. Default is True. 1436 | * @returns {Acc.IAccessible} 1437 | */ 1438 | static ElementFromHandle(hWnd:="", idObject := "Window", activateChromium:=True) { 1439 | if !IsInteger(idObject) 1440 | try idObject := Acc.ObjId.%idObject% 1441 | if !IsInteger(hWnd) 1442 | hWnd := WinExist(hWnd) 1443 | if !hWnd 1444 | throw Error("Invalid window handle or window not found", -1) 1445 | if activateChromium 1446 | Acc.ActivateChromiumAccessibility(hWnd) 1447 | IID := Buffer(16) 1448 | if DllCall("oleacc\AccessibleObjectFromWindow", "ptr",hWnd, "uint",idObject &= 0xFFFFFFFF 1449 | , "ptr",-16 + NumPut("int64", idObject == 0xFFFFFFF0 ? 0x46000000000000C0 : 0x719B3800AA000C81, NumPut("int64", idObject == 0xFFFFFFF0 ? 0x0000000000020400 : 0x11CF3C3D618736E0, IID)) 1450 | , "ptr*", ComObj := ComValue(9,0)) = 0 1451 | Return Acc.IAccessible(ComObj,,hWnd) 1452 | } 1453 | ; Wrapper for native function name 1454 | static ObjectFromWindow(args*) => Acc.ElementFromHandle(args*) 1455 | /** 1456 | * Returns an Acc element corresponding to the provided windows Chrome_RenderWidgetHostHWND control. 1457 | * @param hWnd The window Hwnd. Default is Last Found Window. 1458 | * @param activateChromium Whether to turn on accessibility. Default is True. 1459 | * @returns {Acc.IAccessible} 1460 | */ 1461 | static ElementFromChromium(hWnd:="", activateChromium:=True) { 1462 | if !IsInteger(hWnd) 1463 | hWnd := WinExist(hWnd) 1464 | if !hWnd 1465 | throw Error("Invalid window handle or window not found", -1) 1466 | if activateChromium 1467 | Acc.ActivateChromiumAccessibility(hWnd) 1468 | if !(cHwnd := ControlGetHwnd("Chrome_RenderWidgetHostHWND1", hWnd)) 1469 | throw Error("Chromium render element was not found", -1) 1470 | return Acc.ElementFromHandle(cHwnd, -4,False) 1471 | } 1472 | static ObjectFromChromium(args*) => Acc.ElementFromChromium(args*) 1473 | /** 1474 | * Returns an Acc element from a path string (comma-separated integers or RoleText values) 1475 | * @param ChildPath Comma-separated indexes for the tree traversal. 1476 | * Instead of an index, RoleText is also permitted. 1477 | * @param hWnd Window handle or IAccessible object. Default is Last Found Window. 1478 | * @param activateChromium Whether to turn on accessibility for Chromium-based windows. Default is True. 1479 | * @returns {Acc.IAccessible} 1480 | */ 1481 | static ElementFromPath(ChildPath, hWnd:="", activateChromium:=True) { 1482 | if Type(hWnd) = "Acc.IAccessible" 1483 | oAcc := hWnd 1484 | else { 1485 | if activateChromium 1486 | Acc.ActivateChromiumAccessibility(hWnd) 1487 | oAcc := Acc.ElementFromHandle(hWnd) 1488 | } 1489 | ChildPath := StrReplace(StrReplace(ChildPath, ".", ","), " ") 1490 | Loop Parse ChildPath, "," 1491 | { 1492 | if IsInteger(A_LoopField) 1493 | oAcc := oAcc.GetNthChild(A_LoopField) 1494 | else { 1495 | RegExMatch(A_LoopField, "(\D+)(\d*)", &m), i := m[2] || 1, c := 0 1496 | if m[1] = "p" { 1497 | Loop i 1498 | oAcc := oAcc.Parent 1499 | continue 1500 | } 1501 | for oChild in oAcc { 1502 | try { 1503 | if (StrReplace(oChild.RoleText, " ") = m[1]) && (++c = i) { 1504 | oAcc := oChild 1505 | break 1506 | } 1507 | } 1508 | } 1509 | } 1510 | } 1511 | Return oAcc 1512 | } 1513 | static ObjectFromPath(args*) => Acc.ElementFromPath(args*) 1514 | /** 1515 | * Internal method. Used to get an Acc element returned from an event. 1516 | * @param hWnd Window/control handle 1517 | * @param idObject Object ID of the object that generated the event 1518 | * @param idChild Specifies whether the event was triggered by an object or one of its child elements. 1519 | * @returns {Acc.IAccessible} 1520 | */ 1521 | static ObjectFromEvent(hWnd, idObject, idChild) { 1522 | if (DllCall("oleacc\AccessibleObjectFromEvent" 1523 | , "Ptr", hWnd 1524 | , "UInt", idObject 1525 | , "UInt", idChild 1526 | , "Ptr*", pacc := ComValue(9,0) 1527 | , "Ptr", varChild := Buffer(16)) = 0) { 1528 | return Acc.IAccessible(pacc, NumGet(varChild, 8, "UInt"), DllCall("GetAncestor", "Ptr", hWnd, "UInt", 2)) 1529 | } 1530 | throw Error("ObjectFromEvent failed", -1) 1531 | } 1532 | /** 1533 | * Returns the root element (Acc element for the desktop) 1534 | * @returns {Acc.IAccessible} 1535 | */ 1536 | static GetRootElement() { 1537 | return Acc.ElementFromHandle(0x10010) 1538 | } 1539 | /** 1540 | * Activates accessibility in a Chromium-based window. 1541 | * The WM_GETOBJECT message is sent to the Chrome_RenderWidgetHostHWND1 control 1542 | * and the render elements' Name property is accessed. Once the message is sent, the method 1543 | * will wait up to 500ms until accessibility is enabled. 1544 | * For a specific Hwnd, accessibility will only be tried to turn on once, and regardless of 1545 | * whether it was successful or not, later calls of this method with that Hwnd will simply return. 1546 | * @returns True if accessibility was successfully turned on. 1547 | */ 1548 | static ActivateChromiumAccessibility(hWnd:="") { 1549 | static activatedHwnds := Map() 1550 | if !IsInteger(hWnd) 1551 | hWnd := WinExist(hWnd) 1552 | if activatedHwnds.Has(hWnd) 1553 | return 1 1554 | activatedHwnds[hWnd] := 1, cHwnd := 0 1555 | try cHwnd := ControlGetHwnd("Chrome_RenderWidgetHostHWND1", hWnd) 1556 | if !cHwnd 1557 | return 1558 | SendMessage(WM_GETOBJECT := 0x003D, 0, 1,, cHwnd) 1559 | try { 1560 | rendererEl := Acc.ElementFromHandle(cHwnd,,False).FindElement({Role:15}, 5) 1561 | _ := rendererEl.Name ; it doesn't work without calling CurrentName (at least in Skype) 1562 | } 1563 | 1564 | waitTime := A_TickCount + 500 1565 | while IsSet(rendererEl) && (A_TickCount < waitTime) { 1566 | try { 1567 | if rendererEl.Value 1568 | return 1 1569 | } 1570 | Sleep 20 1571 | } 1572 | } 1573 | ; Internal method to query an IAccessible pointer 1574 | static Query(pAcc) { 1575 | oCom := ComObjQuery(pAcc, "{618736e0-3c3d-11cf-810c-00aa00389b71}") 1576 | ObjAddRef(oCom.ptr) 1577 | Try Return ComValue(9, oCom.ptr) 1578 | } 1579 | ; Internal method to get the RoleText from Role integer 1580 | static GetRoleText(nRole) { 1581 | if !IsInteger(nRole) { 1582 | if (Type(nRole) = "String") && (nRole != "") 1583 | return nRole 1584 | throw TypeError("The specified role is not an integer!",-1) 1585 | } 1586 | nRole := Integer(nRole) 1587 | nSize := DllCall("oleacc\GetRoleText", "Uint", nRole, "Ptr", 0, "Uint", 0) 1588 | VarSetStrCapacity(&sRole, nSize+2) 1589 | DllCall("oleacc\GetRoleText", "Uint", nRole, "str", sRole, "Uint", nSize+2) 1590 | Return sRole 1591 | } 1592 | ; Internal method to get the StateText from State integer 1593 | static GetStateText(nState) { 1594 | nSize := DllCall("oleacc\GetStateText" 1595 | , "Uint" , nState 1596 | , "Ptr" , 0 1597 | , "Uint" , 0) 1598 | VarSetStrCapacity(&sState, nSize+2) 1599 | DllCall("oleacc\GetStateText" 1600 | , "Uint" , nState 1601 | , "str" , sState 1602 | , "Uint" , nSize+2) 1603 | return sState 1604 | } 1605 | /** 1606 | * Registers an event to the provided callback function. 1607 | * Returns an event handler object, that once destroyed will unhook the event. 1608 | * @param callback The callback function with two mandatory arguments: CallbackFunction(oAcc, EventInfo) 1609 | * @param eventMin One of the Acc.Event constants 1610 | * @param eventMax Optional: one of the Acc.Event constants, which if provided will register 1611 | * a range of events from eventMin to eventMax 1612 | * @param PID Optional: Process ID from which to register events. Default is all processes. 1613 | * @returns {Object} 1614 | */ 1615 | static RegisterWinEvent(callback, eventMin, eventMax?, PID:=0) { 1616 | if HasMethod(eventMin) ; Legacy support: if eventMin is a method, then the arguments are: event, callback, PID 1617 | PID := eventMax ?? PID, eventMax := callback, callback := eventMin, eventMin := eventMax 1618 | if IsSet(eventMax) && HasMethod(eventMax) ; Legacy support: if eventMax is a method, then the arguments are: eventMin, eventMax, callback, PID 1619 | callbackBuf := eventMax, eventMax := eventMin, eventMin := callback, callback := callbackBuf 1620 | if !IsSet(eventMax) 1621 | eventMax := eventMin 1622 | if Type(eventMin) = "String" 1623 | try eventMin := Acc.Event.%eventMin% 1624 | if Type(eventMax) = "String" 1625 | try eventMax := Acc.Event.%eventMax% 1626 | pCallback := CallbackCreate(this.GetMethod("HandleWinEvent").Bind(this, callback),, 7) 1627 | hook := Acc.SetWinEventHook(eventMin, eventMax, pCallback, PID) 1628 | return {__Hook:hook, __Callback:pCallback, __Delete:{ call: (*) => (this.UnhookWinEvent(hook), CallbackFree(pCallback)) }} 1629 | } 1630 | ; Internal method. Calls the callback function after wrapping the IAccessible native object 1631 | static HandleWinEvent(fCallback, hWinEventHook, Event, hWnd, idObject, idChild, dwEventThread, dwmsEventTime) { 1632 | idObject := idObject << 32 >> 32, idChild := idChild << 32 >> 32, event &= 0xFFFFFFFF, dwEventThread &= 0xFFFFFFFF, dwmsEventTime &= 0xFFFFFFFF ; convert to INT/UINT 1633 | try oAcc := Acc.ObjectFromEvent(hWnd, idObject, idChild) 1634 | catch 1635 | return 0 1636 | return fCallback(oAcc, {Event:Event, EventThread:dwEventThread, EventTime:dwmsEventTime, ControlID:hWnd, WinID:oAcc.WinID, ObjId:idObject}) 1637 | } 1638 | ; Internal method. Hooks a range of events to a callback function. 1639 | static SetWinEventHook(eventMin, eventMax, pCallback, PID:=0) { 1640 | DllCall("ole32\CoInitialize", "Uint", 0) 1641 | Return DllCall("SetWinEventHook", "Uint", eventMin, "Uint", eventMax, "Uint", 0, "UInt", pCallback, "Uint", PID, "Uint", 0, "Uint", 0) 1642 | } 1643 | ; Internal method. Unhooks a WinEventHook. 1644 | static UnhookWinEvent(hHook) { 1645 | Return DllCall("UnhookWinEvent", "Ptr", hHook) 1646 | } 1647 | /** 1648 | * Returns the Hwnd to a window from a set of screen coordinates 1649 | * @param X Screen X-coordinate 1650 | * @param Y Screen Y-coordinate 1651 | */ 1652 | static WindowFromPoint(X, Y) { ; by SKAN and Linear Spoon 1653 | return DllCall("GetAncestor", "Ptr", DllCall("user32.dll\WindowFromPoint", "Int64", Y << 32 | (X & 0xFFFFFFFF)), "UInt", 2) 1654 | } 1655 | /** 1656 | * Removes all highlights created by Element.Highlight() 1657 | */ 1658 | static ClearHighlights() { 1659 | for _, p in Acc.__HighlightGuis { 1660 | for __, r in p 1661 | r.Destroy() 1662 | } 1663 | Acc.__HighlightGuis := Map() 1664 | } 1665 | 1666 | ; Internal class: AccViewer code 1667 | class Viewer { 1668 | __New() { 1669 | this.Stored := {mwId:0, FilteredTreeView:Map(), TreeView:Map()} 1670 | this.Capturing := False 1671 | this.gViewer := Gui("AlwaysOnTop Resize","AccViewer") 1672 | this.gViewer.OnEvent("Close", (*) => ExitApp()) 1673 | this.gViewer.OnEvent("Size", this.GetMethod("gViewer_Size").Bind(this)) 1674 | this.gViewer.Add("Text", "w100", "Window Info").SetFont("bold") 1675 | this.LVWin := this.gViewer.Add("ListView", "h140 w250", ["Property", "Value"]) 1676 | this.LVWin.OnEvent("ContextMenu", LV_CopyTextMethod := this.GetMethod("LV_CopyText").Bind(this)) 1677 | this.LVWin.ModifyCol(1,60) 1678 | this.LVWin.ModifyCol(2,180) 1679 | for _, v in ["Title", "Text", "Id", "Location", "Class(NN)", "Process", "PID"] 1680 | this.LVWin.Add(,v,"") 1681 | this.gViewer.Add("Text", "w100", "Acc Info").SetFont("bold") 1682 | this.LVProps := this.gViewer.Add("ListView", "h220 w250", ["Property", "Value"]) 1683 | this.LVProps.OnEvent("ContextMenu", LV_CopyTextMethod) 1684 | this.LVProps.ModifyCol(1,100) 1685 | this.LVProps.ModifyCol(2,140) 1686 | for _, v in ["RoleText", "Role", "Value", "Name", "Location", "StateText", "State", "DefaultAction", "Description", "KeyboardShortcut", "Help", "ChildId", "Identity"] 1687 | this.LVProps.Add(,v,"") 1688 | this.ButCapture := this.gViewer.Add("Button", "xp+60 y+10 w130", "Start capturing (F1)") 1689 | this.ButCapture.OnEvent("Click", this.CaptureHotkeyFunc := this.GetMethod("ButCapture_Click").Bind(this)) 1690 | HotKey("~F1", this.CaptureHotkeyFunc) 1691 | this.SBMain := this.gViewer.Add("StatusBar",, " Start capturing, then hold cursor still to construct tree") 1692 | this.SBMain.OnEvent("Click", this.GetMethod("SBMain_Click").Bind(this)) 1693 | this.SBMain.OnEvent("ContextMenu", this.GetMethod("SBMain_Click").Bind(this)) 1694 | this.gViewer.Add("Text", "x278 y10 w100", "Acc Tree").SetFont("bold") 1695 | this.TVAcc := this.gViewer.Add("TreeView", "x275 y25 w250 h390 -0x800") 1696 | this.TVAcc.OnEvent("Click", this.GetMethod("TVAcc_Click").Bind(this)) 1697 | this.TVAcc.OnEvent("ContextMenu", this.GetMethod("TVAcc_ContextMenu").Bind(this)) 1698 | this.TVAcc.Add("Start capturing to show tree") 1699 | this.TextFilterTVAcc := this.gViewer.Add("Text", "x275 y428", "Filter:") 1700 | this.EditFilterTVAcc := this.gViewer.Add("Edit", "x305 y425 w100") 1701 | this.EditFilterTVAcc.OnEvent("Change", this.GetMethod("EditFilterTVAcc_Change").Bind(this)) 1702 | this.gViewer.Show() 1703 | } 1704 | ; Resizes window controls when window is resized 1705 | gViewer_Size(GuiObj, MinMax, Width, Height) { 1706 | this.TVAcc.GetPos(&TVAccX, &TVAccY, &TVAccWidth, &TVAccHeight) 1707 | this.TVAcc.Move(,,Width-TVAccX-10,Height-TVAccY-50) 1708 | this.TextFilterTVAcc.Move(TVAccX, Height-42) 1709 | this.EditFilterTVAcc.Move(TVAccX+30, Height-45) 1710 | this.TVAcc.GetPos(&LVPropsX, &LVPropsY, &LVPropsWidth, &LVPropsHeight) 1711 | this.LVProps.Move(,,,Height-LVPropsY-225) 1712 | this.ButCapture.Move(,Height -55) 1713 | } 1714 | ; Starts showing the element under the cursor with 200ms intervals with CaptureCallback 1715 | ButCapture_Click(GuiCtrlObj?, Info?) { 1716 | if this.Capturing { 1717 | this.StopCapture() 1718 | return 1719 | } 1720 | this.Capturing := True 1721 | HotKey("~F1", this.CaptureHotkeyFunc, "Off") 1722 | HotKey("~Esc", this.CaptureHotkeyFunc, "On") 1723 | this.TVAcc.Delete() 1724 | this.TVAcc.Add("Hold cursor still to construct tree") 1725 | this.ButCapture.Text := "Stop capturing (Esc)" 1726 | this.CaptureCallback := this.GetMethod("CaptureCycle").Bind(this) 1727 | SetTimer(this.CaptureCallback, 200) 1728 | } 1729 | ; Handles right-clicking a listview (copies to clipboard) 1730 | LV_CopyText(GuiCtrlObj, Info, *) { 1731 | LVData := Info > GuiCtrlObj.GetCount() 1732 | ? ListViewGetContent("", GuiCtrlObj) 1733 | : ListViewGetContent("Selected", GuiCtrlObj) 1734 | for LVData in StrSplit(LVData, "`n") { 1735 | LVData := StrSplit(LVData, "`t",,2) 1736 | if LVData.Length < 2 1737 | continue 1738 | out .= ", " (GuiCtrlObj.Hwnd = this.LVWin.Hwnd ? "" : LVData[1] ": ") (LVData[1] ~= "Role$|State$|ChildId" ? LVData[2] : LVData[1] = "Location" ? "{" LVData[2] "}" : "`"" StrReplace(StrReplace(LVData[2], "``", "````"), "`"", "```"") "`"") 1739 | } 1740 | ToolTip("Copied: " (A_Clipboard := SubStr(out, 3))) 1741 | SetTimer((*) => ToolTip(), -3000) 1742 | } 1743 | ; Copies the Acc path to clipboard when statusbar is clicked 1744 | SBMain_Click(GuiCtrlObj, Info, *) { 1745 | if InStr(this.SBMain.Text, "Path:") { 1746 | ToolTip("Copied: " (A_Clipboard := SubStr(this.SBMain.Text, 9))) 1747 | SetTimer((*) => ToolTip(), -3000) 1748 | } 1749 | } 1750 | ; Stops capturing elements under mouse, unhooks CaptureCallback 1751 | StopCapture(GuiCtrlObj:=0, Info:=0) { 1752 | if this.Capturing { 1753 | this.Capturing := False 1754 | this.ButCapture.Text := "Start capturing (F1)" 1755 | HotKey("~Esc", this.CaptureHotkeyFunc, "Off") 1756 | HotKey("~F1", this.CaptureHotkeyFunc, "On") 1757 | SetTimer(this.CaptureCallback, 0) 1758 | this.Stored.oAcc.Highlight() 1759 | return 1760 | } 1761 | } 1762 | ; Gets Acc element under mouse, updates the GUI. 1763 | ; If the mouse is not moved for 1 second then constructs the Acc tree. 1764 | CaptureCycle() { 1765 | MouseGetPos(&mX, &mY, &mwId) 1766 | oAcc := Acc.ElementFromPoint() 1767 | if this.Stored.HasOwnProp("oAcc") && IsObject(oAcc) && oAcc.IsEqual(this.Stored.oAcc) { 1768 | if this.FoundTime != 0 && ((A_TickCount - this.FoundTime) > 1000) { 1769 | if (mX == this.Stored.mX) && (mY == this.Stored.mY) 1770 | this.ConstructTreeView(), this.FoundTime := 0 1771 | else 1772 | this.FoundTime := A_TickCount 1773 | } 1774 | this.Stored.mX := mX, this.Stored.mY := mY 1775 | return 1776 | } 1777 | this.LVWin.Delete() 1778 | WinGetPos(&mwX, &mwY, &mwW, &mwH, mwId) 1779 | propsOrder := ["Title", "Text", "Id", "Location", "Class(NN)", "Process", "PID"] 1780 | props := Map("Title", WinGetTitle(mwId), "Text", WinGetText(mwId), "Id", mwId, "Location", "x: " mwX " y: " mwY " w: " mwW " h: " mwH, "Class(NN)", WinGetClass(mwId), "Process", WinGetProcessName(mwId), "PID", WinGetPID(mwId)) 1781 | for propName in propsOrder 1782 | this.LVWin.Add(,propName,props[propName]) 1783 | this.LVProps_Populate(oAcc) 1784 | this.Stored.mwId := mwId, this.Stored.oAcc := oAcc, this.Stored.mX := mX, this.Stored.mY := mY, this.FoundTime := A_TickCount 1785 | } 1786 | ; Populates the listview with Acc element properties 1787 | LVProps_Populate(oAcc) { 1788 | Acc.ClearHighlights() ; Clear 1789 | oAcc.Highlight(0) ; Indefinite show 1790 | this.LVProps.Delete() 1791 | Location := {x:"N/A",y:"N/A",w:"N/A",h:"N/A"}, RoleText := "N/A", Role := "N/A", Value := "N/A", Name := "N/A", StateText := "N/A", State := "N/A", DefaultAction := "N/A", Description := "N/A", KeyboardShortcut := "N/A", Help := "N/A", ChildId := "", Identity := "N/A" 1792 | for _, v in ["RoleText", "Role", "Value", "Name", "Location", "StateText", "State", "DefaultAction", "Description", "KeyboardShortcut", "Help", "ChildId", "Identity"] { 1793 | try %v% := oAcc.%v% 1794 | this.LVProps.Add(,v, v = "Location" ? ("x: " %v%.x " y: " %v%.y " w: " %v%.w " h: " %v%.h) : %v%) 1795 | } 1796 | } 1797 | ; Handles selecting elements in the Acc tree, highlights the selected element 1798 | TVAcc_Click(GuiCtrlObj, Info) { 1799 | if this.Capturing 1800 | return 1801 | try oAcc := this.EditFilterTVAcc.Value ? this.Stored.FilteredTreeView[Info] : this.Stored.TreeView[Info] 1802 | if IsSet(oAcc) && oAcc { 1803 | try this.SBMain.SetText(" Path: " oAcc.Path) 1804 | this.LVProps_Populate(oAcc) 1805 | } 1806 | } 1807 | ; Permits copying the Dump of Acc element(s) to clipboard 1808 | TVAcc_ContextMenu(GuiCtrlObj, Item, IsRightClick, X, Y) { 1809 | TVAcc_Menu := Menu() 1810 | try oAcc := this.EditFilterTVAcc.Value ? this.Stored.FilteredTreeView[Item] : this.Stored.TreeView[Item] 1811 | if IsSet(oAcc) 1812 | TVAcc_Menu.Add("Copy to Clipboard", (*) => A_Clipboard := oAcc.Dump()) 1813 | TVAcc_Menu.Add("Copy Tree to Clipboard", (*) => A_Clipboard := Acc.ElementFromHandle(this.Stored.mwId).DumpAll()) 1814 | TVAcc_Menu.Show() 1815 | } 1816 | ; Handles filtering the Acc elements inside the TreeView when the text hasn't been changed in 500ms. 1817 | ; Sorts the results by Acc properties. 1818 | EditFilterTVAcc_Change(GuiCtrlObj, Info, *) { 1819 | static TimeoutFunc := "", ChangeActive := False 1820 | if !this.Stored.TreeView.Count 1821 | return 1822 | if (Info != "DoAction") || ChangeActive { 1823 | if !TimeoutFunc 1824 | TimeoutFunc := this.GetMethod("EditFilterTVAcc_Change").Bind(this, GuiCtrlObj, "DoAction") 1825 | SetTimer(TimeoutFunc, -500) 1826 | return 1827 | } 1828 | ChangeActive := True 1829 | this.Stored.FilteredTreeView := Map(), parents := Map() 1830 | if !(searchPhrase := this.EditFilterTVAcc.Value) { 1831 | this.ConstructTreeView() 1832 | ChangeActive := False 1833 | return 1834 | } 1835 | this.TVAcc.Delete() 1836 | temp := this.TVAcc.Add("Searching...") 1837 | Sleep -1 1838 | this.TVAcc.Opt("-Redraw") 1839 | this.TVAcc.Delete() 1840 | for index, oAcc in this.Stored.TreeView { 1841 | for _, prop in ["RoleText", "Role", "Value", "Name", "StateText", "State", "DefaultAction", "Description", "KeyboardShortcut", "Help", "ChildId", "Identity"] { 1842 | try { 1843 | if InStr(oAcc.%Prop%, searchPhrase) { 1844 | if !parents.Has(prop) 1845 | parents[prop] := this.TVAcc.Add(prop,, "Expand") 1846 | this.Stored.FilteredTreeView[this.TVAcc.Add(this.GetShortDescription(oAcc), parents[prop], "Expand")] := oAcc 1847 | } 1848 | } 1849 | } 1850 | } 1851 | if !this.Stored.FilteredTreeView.Count 1852 | this.TVAcc.Add("No results found matching `"" searchPhrase "`"") 1853 | this.TVAcc.Opt("+Redraw") 1854 | TimeoutFunc := "", ChangeActive := False 1855 | } 1856 | ; Populates the TreeView with the Acc tree when capturing and the mouse is held still 1857 | ConstructTreeView() { 1858 | this.TVAcc.Delete() 1859 | this.TVAcc.Add("Constructing Tree, please wait...") 1860 | Sleep -1 1861 | this.TVAcc.Opt("-Redraw") 1862 | this.TVAcc.Delete() 1863 | this.Stored.TreeView := Map() 1864 | this.RecurseTreeView(Acc.ElementFromHandle(this.Stored.mwId)) 1865 | this.TVAcc.Opt("+Redraw") 1866 | for k, v in this.Stored.TreeView 1867 | if this.Stored.oAcc.IsEqual(v) 1868 | this.TVAcc.Modify(k, "Vis Select"), this.SBMain.SetText(" Path: " v.Path) 1869 | } 1870 | ; Stores the Acc tree with corresponding path values for each element 1871 | RecurseTreeView(oAcc, parent:=0, path:="") { 1872 | this.Stored.TreeView[TWEl := this.TVAcc.Add(this.GetShortDescription(oAcc), parent, "Expand")] := oAcc.DefineProp("Path", {value:path}) 1873 | for k, v in oAcc 1874 | this.RecurseTreeView(v, TWEl, path (path?",":"") k) 1875 | } 1876 | ; Creates a short description string for the Acc tree elements 1877 | GetShortDescription(oAcc) { 1878 | elDesc := " `"`"" 1879 | try elDesc := " `"" oAcc.Name "`"" 1880 | try elDesc := oAcc.RoleText elDesc 1881 | catch 1882 | elDesc := "`"`"" elDesc 1883 | return elDesc 1884 | } 1885 | } 1886 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Acc-v2 2 | Acc library for AHK v2 3 | 4 | 5 | ## Notable changes (compared to Acc v1): 6 | Acc v2 in not a port of AHK v1 Acc library, but instead a complete redesign to incorporate more object-oriented approaches. 7 | 8 | 1) All Acc elements are now array-like objects, where the "Length" property contains the number of children, 9 | any nth children can be accessed with element[n], and children can be iterated over with for loops. 10 | 2) Acc main functions are contained in the global Acc class/variable 11 | 3) Element methods are contained inside element objects 12 | 4) Element properties can be used without the "acc" prefix 13 | 5) ChildIds have been removed (are handled in the backend), but can be accessed through el.ChildId 14 | 6) Additional methods have been added for elements, such as FindElement, FindElements, Click 15 | 7) Acc constants are included in the Acc object 16 | 8) AccViewer is built into the library: when ran directly the AccViewer will show, when included 17 | in another script then it won't show (but can be accessed by calling Acc.Viewer()) 18 | 9) Some main Acc methods have been given aliases: eg ObjectFromWindow -> ElementFromHandle. 19 | 20 | ## Short introduction 21 | Acc (otherwise known as IAccessible or MSAA) is a library to get information about (and sometimes interact with) windows, controls, and window elements that are otherwise not accessible with AHKs Control functions. 22 | 23 | When Acc.ahk is ran alone, it displays the AccViewer: a window inspecter to get Acc information from elements. In the AccViewer on the left side are displayed all the available properties for elements (Value, Name etc), and on the right side will be displayed the Acc tree which shows how elements are related to eachother. In the bottom is displayed the Acc path (right-click to copy it), which can be used to get that specific element with the Acc library. 24 | 25 | To get started with Acc, first [include](https://lexikos.github.io/v2/docs/commands/_Include.htm) it in your program with [#include](https://lexikos.github.io/v2/docs/commands/_Include.htm). If Acc.ahk is in the same folder as your script use `#include Acc.ahk`, if it is in the Lib folder then use `#include `. 26 | 27 | All main Acc properties and methods are accessible through the global variable `Acc`, which is created when Acc.ahk is included in your script. 28 | To access Acc elements, first you need to get a starting point element (usually a window) and save it in a variable: something like `oAcc := Acc.ElementFromHandle(WinTitle)`. WinTitle uses the same rules as any other AHK function, so SetTitleMatchMode also applies to it. 29 | 30 | To get elements from the window element, you can use the Acc path from AccViewer: for example `oEl := oAcc[4,1,4]` would get the windows 4th sub-element, then the sub-elements 1st child, and then its 4th child. 31 | 32 | All the properties displayed in AccViewer can be accessed from the element: `oEl.Name` will get that elements' name or throw an error if the name doesn't exist. Most properties are read-only, but the Value property can sometimes be changed with `oEl.Value := "newvalue"` 33 | 34 | Element methods can be used in the same way. To do the default action (usually clicking), use `oEl.DoDefaultAction()`, to highlight the element for 2 seconds use `oEl.Highlight(2000)`. Dump info about a specific element with `oEl.Dump()` and dump info about sub-elements as well with `oEl.DumpAll()` (to get paths and info about all elements in a window, use it on the window element: `MsgBox( Acc.ElementFromHandle(WinTitle).DumpAll() )` 35 | 36 | Some examples of how Acc.ahk can be used are included in the Examples folder. 37 | 38 | # Acc methods 39 | 40 | ElementFromPoint(x:=unset, y:=unset, &idChild := "", activateChromium := True) 41 | Gets an Acc element from screen coordinates X and Y (NOT relative to the active window). 42 | ElementFromHandle(hWnd:="A", idObject := 0, activateChromium := True) 43 | Gets an Acc element from a WinTitle, by default the active window. This can also be a Control handle. 44 | Additionally idObject can be specified from Acc.ObjId constants (eg to get the Caret location). 45 | GetRootElement() 46 | Gets the Acc element for the Desktop 47 | ActivateChromiumAccessibility(hWnd) 48 | Sends the WM_GETOBJECT message to the Chromium document element and waits for the 49 | app to be accessible to Acc. This is called when ObjectFromPoint or ObjectFromWindow 50 | activateChromium flag is set to True. A small performance increase may be gotten 51 | if that flag is set to False when it is not needed. 52 | RegisterWinEvent(event, callback, PID:=0) 53 | RegisterWinEvent(eventMin, eventMax, callback, PID:=0) 54 | Registers an event or event range from Acc.Event to a callback function and returns 55 | a new object containing the WinEventHook 56 | EventMax is an optional variable: if only eventMin and callback are provided, then 57 | only that single event is registered. If all three arguments are provided, then 58 | an event range from eventMin to eventMax are registered to the callback function. 59 | The callback function needs to have two arguments: 60 | CallbackFunction(oAcc, EventInfo) 61 | 62 | When the callback function is called: 63 | oAcc will be the Acc element that called the event 64 | EventInfo will be an object containing the following properties: 65 | Event - an Acc.Event constant 66 | EventTime - when the event was triggered in system time 67 | WinID - handle of the window that sent the event 68 | ControlID - handle of the control that sent the event, which depending on the 69 | window will be the window itself or a control 70 | ObjId - the object Id (Acc.ObjId) the event was called with 71 | PID is the Process ID of the process/window the events will be registered from. By default 72 | events from all windows are registered. 73 | Unhooking of the event handler will happen once the returned object is destroyed 74 | (either when overwritten by a constant, or when the script closes). 75 | 76 | Legacy methods: 77 | SetWinEventHook(eventMin, eventMax, pCallback) 78 | UnhookWinEvent(hHook) 79 | ElementFromPath(ChildPath, hWnd:="A") 80 | Same as ElementFromHandle[comma-separated path] 81 | GetRoleText(nRole) 82 | Same as element.RoleText 83 | GetStateText(nState) 84 | Same as element.StateText 85 | Query(pAcc) 86 | For internal use 87 | 88 | # Acc constants 89 | 90 | Constants can be accessed as properties (eg Acc.OBJID.CARET), or the property name can be 91 | accessed by getting as an item (eg Acc.OBJID[0xFFFFFFF8]) 92 | 93 | ObjId - object identifiers that identify categories of accessible objects within a window. 94 | State - used to describe the state of objects in an application UI. These are returned by Element.State or Element.StateText. 95 | Role - used to describe the roles of various UI objects in an application. These are returned by Element.Role or Element.RoleText. 96 | NavDir - indicate the spatial (up, down, left, and right) or logical (first child, 97 | last, next, and previous) direction used with Element.Navigate() to navigate from one 98 | user interface element to another within the same container. 99 | SelectionFlag - used to specify how an accessible object becomes selected or takes the focus. 100 | These are used by Element.Select(). 101 | Event - events that are generated by the operating system and by applications. These are 102 | used when dealing with RegisterWinEvent. 103 | 104 | More thorough explanations for the constants are available [in Microsoft documentations](https://docs.microsoft.com/en-us/windows/win32/winauto/constants-and-enumerated-types). 105 | 106 | # Acc element properties 107 | Element[n] => Gets the nth element. Multiple of these can be used like a path: 108 | Element[4,1,4] will select 4th childs 1st childs 4th child 109 | Conditions (see ValidateCondition) are supported: 110 | Element[4,{Name:"Something"}] will select the fourth childs first child matching the name "Something" 111 | Conditions also accept an index (or i) parameter to select from multiple similar elements 112 | Element[{Name:"Something", i:3}] selects the third element of elements with name "Something" 113 | Negative index will select from the last element 114 | Element[{Name:"Something", i:-1}] selects the last element of elements with name "Something" 115 | Name => Gets or sets the name. All objects support getting this property. 116 | Value => Gets or sets the value. Not all objects have a value. 117 | Role => Gets the Role of the specified object in integer form. All objects support this property. 118 | RoleText => Role converted into text form. All objects support this property. 119 | Help => Retrieves the Help property string of an object. Not all objects support this property. 120 | KeyboardShortcut => Retrieves the specified object's shortcut key or access key. Not all objects support this property. 121 | State => Retrieves the current state in integer form. All objects support this property. 122 | StateText => State converted into text form 123 | Description => Retrieves a string that describes the visual appearance of the specified object. Not all objects have a description. 124 | DefaultAction => Retrieves a string that indicates the object's default action. Not all objects have a default action. 125 | Focus => Returns the focused child element (or itself). 126 | If no child is focused, an error is thrown 127 | Selection => Retrieves the selected children of this object. All objects that support selection must support this property. 128 | Parent => Returns the parent element. All objects support this property. 129 | IsChild => Checks whether the current element is of child type (this is usually not needed) 130 | Length => Returns the number of children the element has 131 | Location => Returns the object's current screen location in an object {x,y,w,h} 132 | Children => Returns all children as an array (usually not required) 133 | Exists => Checks whether the element is still alive and accessible 134 | ControlID => ID (hwnd) of the control associated with the element 135 | WinID => ID of the window the element belongs to 136 | accessible => ComObject of the underlying IAccessible (this is usually not needed) 137 | childId => childId of the underlying IAccessible (this is usually not needed) 138 | 139 | # Acc element methods 140 | 141 | Select(flags) 142 | Modifies the selection or moves the keyboard focus of the specified object. flags can be any of the Acc.SelectionFlag constants 143 | DoDefaultAction() 144 | Performs the specified object's default action. Not all objects have a default action. 145 | GetNthChild(n) 146 | This is equal to oElement[n] 147 | GetLocation(relativeTo:="") 148 | Returns an object containing the x, y coordinates and width and height: {x:x coordinate, y:y coordinate, w:width, h:height}. 149 | relativeTo can be client, window or screen, default is A_CoordModeMouse. 150 | IsEqual(oCompare) 151 | Checks whether the element is equal to another element (oCompare) 152 | FindElement(condition, scope:=4, index:=1, order:=0, depth:=-1) 153 | Condition: A condition object (see ValidateCondition). This condition object can also contain named argument values: 154 | FindElement({Name:"Something", scope:"Subtree"}) 155 | Scope: the search scope (Acc.SCOPE value): Element, Children, Family (Element+Children), Descendants, SubTree (Element+Descendants). Default is Descendants. 156 | Index: can be used to search for i-th element. 157 | Like the other parameters, this can also be supplied in the condition with index or i: 158 | FindElement({Name:"Something", i:3}) finds the third element with name "Something" 159 | Negative index reverses the search direction: 160 | FindElement({Name:"Something", i:-1}) finds the last element with name "Something" 161 | Since index/i needs to be a key-value pair, then to use it with an "or" condition 162 | it must be inside an object ("and" condition), for example with key "or": 163 | FindElement({or:[{Name:"Something"}, {Name:"Something else"}], index:2}) 164 | Order: defines the order of tree traversal (Acc.TreeTraversalOptions value): 165 | Default, LastToFirst, PostOrder. Default is FirstToLast and PreOrder. 166 | FindElements(condition:=True, scope:=4, depth:=-1) 167 | Returns an array of elements matching the condition (see description under ValidateCondition) 168 | The returned elements also have the "Path" property with the found elements path 169 | WaitElement(conditionOrPath, timeOut:=-1, scope:=4, index:=1, order:=0, depth:=-1) 170 | Waits an element to be detectable in the Acc tree. This doesn't mean that the element 171 | is visible or interactable, use WaitElementExist for that. 172 | Timeout less than 1 waits indefinitely, otherwise is the wait time in milliseconds 173 | A timeout returns 0. 174 | WaitElementExist(conditionOrPath, timeOut:=-1, scope:=4, index:=1, order:=0, depth:=-1) 175 | Waits an element exist that matches a condition or a path. 176 | Timeout less than 1 waits indefinitely, otherwise is the wait time in milliseconds 177 | A timeout returns 0. 178 | Normalize(condition) 179 | Checks whether the current element or any of its ancestors match the condition, 180 | and returns that element. If no element is found, an error is thrown. 181 | ValidateCondition(condition) 182 | Checks whether the element matches a provided condition. 183 | Everything inside {} is an "and" condition, or a singular condition with options 184 | Everything inside [] is an "or" condition 185 | "not" key creates a not condition 186 | "matchmode" key (short form: "mm") defines the MatchMode: StartsWith, Substring, Exact, RegEx (Acc.MATCHMODE values) 187 | "casesensitive" key (short form: "cs") defines case sensitivity: True=case sensitive; False=case insensitive 188 | Any other key (but recommended is "or") can be used to use "or" condition inside "and" condition. 189 | Additionally, when matching for location then partial matching can be used (eg only width and height) 190 | and relative mode (client, window, screen) can be specified with "relative" or "r". 191 | An empty object {} is used as "unset" or "N/A" value. 192 | 193 | For methods which use this condition, it can also contain named arguments: 194 | oAcc.FindElement({Name:"Something", scope:"Subtree", order:"LastToFirst"}) 195 | is equivalent to FindElement({Name:"Something"}, "Subtree",, "LastToFirst") 196 | is equivalent to FindElement({Name:"Something"}, Acc.TreeScope.SubTree,, Acc.TreeTraversalOptions.LastToFirst) 197 | is equivalent to FindElement({Name:"Something"}, 7,, 1) 198 | 199 | {Name:"Something"} => Name must match "Something" (case sensitive) 200 | {Name:"Something", matchmode:"SubString", casesensitive:False} => Name must contain "Something" anywhere inside the Name, case insensitive. matchmode:"SubString" == matchmode:2 == matchmode:Acc.MatchMode.SubString 201 | {Name:"Something", RoleText:"something else"} => Name must match "Something" and RoleText must match "something else" 202 | [{Name:"Something", Role:42}, {Name:"Something2", RoleText:"something else"}] => Name=="Something" and Role==42 OR Name=="Something2" and RoleText=="something else" 203 | {Name:"Something", not:[{RoleText:"something", mm:"Substring"}, {RoleText:"something else", cs:1}]} => Name must match "something" and RoleText cannot match "something" (with matchmode=Substring == matchmode=2) nor "something else" (casesensitive matching) 204 | {or:[{Name:"Something"},{Name:"Something else"}], or2:[{Role:20},{Role:42}]} 205 | {Location:{w:200, h:100, r:"client"}} => Location must match width 200 and height 100 relative to client 206 | 207 | Dump(scope:=1, delimiter:=" ", depth:=-1) 208 | Outputs relevant information about the element (Name, Value, Location etc) 209 | Scope is the search scope: 1=element itself; 2=direct children; 4=descendants (including children of children); 7=whole subtree (including element) 210 | The scope is additive: 3=element itself and direct children. 211 | DumpAll(delimiter:=" ", depth:=-1) 212 | Outputs relevant information about the element and all descendants of the element. This is equivalent to Dump(7) 213 | Highlight(showTime:=unset, color:="Red", d:=2) 214 | Highlights the element for a chosen period of time 215 | Possible showTime values: 216 | Unset - highlights for 2 seconds, or removes the highlighting 217 | 0 - Indefinite highlighting. If the element object gets destroyed, so does the highlighting. 218 | Positive integer (eg 2000) - will highlight and pause for the specified amount of time in ms 219 | Negative integer - will highlight for the specified amount of time in ms, but script execution will continue 220 | "clear" - removes the highlight unconditionally 221 | color can be any of the Color names or RGB values 222 | d sets the border width 223 | Click(WhichButton:="left", ClickCount:=1, DownOrUp:="", Relative:="") 224 | Click the center of the element. 225 | If WhichButton is a number, then Sleep will be called with that number. Eg Click(200) will sleep 200ms after clicking 226 | If ClickCount is a number >=10, then Sleep will be called with that number. To click 10+ times and sleep after, specify "ClickCount SleepTime". Ex: Click("left", 200) will sleep 200ms after clicking. Ex: Click("left", "20 200") will left-click 20 times and then sleep 200ms. 227 | If Relative is "Rel" or "Relative" then X and Y coordinates are treated as offsets from the current mouse position. Otherwise it expects offset values for both X and Y (eg "-5 10" would offset X by -5 and Y by +10). 228 | ControlClick(WhichButton:="left", ClickCount:=1, Options:="") 229 | ControlClicks the element after getting relative coordinates with GetLocation("client"). 230 | If WhichButton is a number, then a Sleep will be called afterwards. Ex: ControlClick(200) will sleep 200ms after clicking. Same for ControlClick("ahk_id 12345", 200) 231 | Navigate(navDir) 232 | Navigates in one of the directions specified by Acc.NavDir constants. Not all elements implement this method. 233 | HitTest(x, y) 234 | Retrieves the child element or child object that is displayed at a specific point on the screen. 235 | This shouldn't be used, since Acc.ObjectFromPoint uses this internally 236 | 237 | --------------------------------------------------------------------------------