How to get the full ACC path for control on cursor? Topic is solved

Get help with using AutoHotkey and its commands and hotkeys
CyL0N
Posts: 210
Joined: 27 Sep 2018, 09:58

How to get the full ACC path for control on cursor?

28 Sep 2018, 01:29

I use ahkspy/accviewer a lot,and i know how to get all the information except Acc information for use in other scripts but it's really tedious writing the bits for ACC, having to manually figure out the 'Path' for the control ACC is displaying text.

Acc: get text from all window/control elements Is the closet thing i've found to an ACC script that gives you the full path to a control by searching through it's output,but i was looking for something more along the lines of AccViewer that outputs code to retrieve currently active control...

Thanks for any help...

btw,despite this being my first post,i'm quite familiar with ahk,so i'm cool with any pointers as to how i could modify accviewer my self,at the very least,i just don't want to start from zero... I've literally always found what i was looking for so never needed to post on the forum,until now i guess.
live ? long & prosper : regards
tmplinshi
Posts: 1540
Joined: 01 Oct 2013, 14:57

Re: How to get the full ACC path for control on cursor?  Topic is solved

30 Sep 2018, 01:49

The function GetAccPath and GetEnumIndex were extracted from AccViewer.

Code: Select all

#NoEnv
#SingleInstance force
SetBatchLines, -1

F12::
	obj := GetInfoUnderCursor()
	msgbox % obj.path
return

GetInfoUnderCursor() {
	Acc := Acc_ObjectFromPoint(child)
	if !value := Acc.accValue(child)
		value := Acc.accName(child)
	accPath := GetAccPath(acc, hwnd).path
	return {text: value, path: accPath, hwnd: hwnd}
}

GetAccPath(Acc, byref hwnd="") {
	hwnd := Acc_WindowFromObject(Acc)
	WinObj := Acc_ObjectFromWindow(hwnd)
	WinObjPos := Acc_Location(WinObj).pos
	while Acc_WindowFromObject(Parent:=Acc_Parent(Acc)) = hwnd {
		t2 := GetEnumIndex(Acc) "." t2
		if Acc_Location(Parent).pos = WinObjPos
			return {AccObj:Parent, Path:SubStr(t2,1,-1)}
		Acc := Parent
	}
	while Acc_WindowFromObject(Parent:=Acc_Parent(WinObj)) = hwnd
		t1.="P.", WinObj:=Parent
	return {AccObj:Acc, Path:t1 SubStr(t2,1,-1)}
}

GetEnumIndex(Acc, ChildId=0) {
	if Not ChildId {
		ChildPos := Acc_Location(Acc).pos
		For Each, child in Acc_Children(Acc_Parent(Acc))
			if IsObject(child) and Acc_Location(child).pos=ChildPos
				return A_Index
	} 
	else {
		ChildPos := Acc_Location(Acc,ChildId).pos
		For Each, child in Acc_Children(Acc)
			if Not IsObject(child) and Acc_Location(Acc,child).pos=ChildPos
				return A_Index
	}
}

; Acc.ahk

;------------------------------------------------------------------------------
; Acc.ahk Standard Library
; by Sean
; Updated by jethrow:
; 	Modified ComObjEnwrap params from (9,pacc) --> (9,pacc,1)
; 	Changed ComObjUnwrap to ComObjValue in order to avoid AddRef (thanks fincs)
; 	Added Acc_GetRoleText & Acc_GetStateText
; 	Added additional functions - commented below
; 	Removed original Acc_Children function
; last updated 2/19/2012
;------------------------------------------------------------------------------

Acc_Init()
{
	Static	h
	If Not	h
		h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
}
Acc_ObjectFromEvent(ByRef _idChild_, hWnd, idObject, idChild)
{
	Acc_Init()
	If	DllCall("oleacc\AccessibleObjectFromEvent", "Ptr", hWnd, "UInt", idObject, "UInt", idChild, "Ptr*", pacc, "Ptr", VarSetCapacity(varChild,8+2*A_PtrSize,0)*0+&varChild)=0
	Return	ComObjEnwrap(9,pacc,1), _idChild_:=NumGet(varChild,8,"UInt")
}

Acc_ObjectFromPoint(ByRef _idChild_ = "", x = "", y = "")
{
	Acc_Init()
	If	DllCall("oleacc\AccessibleObjectFromPoint", "Int64", x==""||y==""?0*DllCall("GetCursorPos","Int64*",pt)+pt:x&0xFFFFFFFF|y<<32, "Ptr*", pacc, "Ptr", VarSetCapacity(varChild,8+2*A_PtrSize,0)*0+&varChild)=0
	Return	ComObjEnwrap(9,pacc,1), _idChild_:=NumGet(varChild,8,"UInt")
}

Acc_ObjectFromWindow(hWnd, idObject = -4)
{
	Acc_Init()
	If	DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hWnd, "UInt", idObject&=0xFFFFFFFF, "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64"),"Int64"), "Ptr*", pacc)=0
	Return	ComObjEnwrap(9,pacc,1)
}

Acc_WindowFromObject(pacc)
{
	If	DllCall("oleacc\WindowFromAccessibleObject", "Ptr", IsObject(pacc)?ComObjValue(pacc):pacc, "Ptr*", hWnd)=0
	Return	hWnd
}

Acc_GetRoleText(nRole)
{
	nSize := DllCall("oleacc\GetRoleText", "Uint", nRole, "Ptr", 0, "Uint", 0)
	VarSetCapacity(sRole, (A_IsUnicode?2:1)*nSize)
	DllCall("oleacc\GetRoleText", "Uint", nRole, "str", sRole, "Uint", nSize+1)
	Return	sRole
}

Acc_GetStateText(nState)
{
	nSize := DllCall("oleacc\GetStateText", "Uint", nState, "Ptr", 0, "Uint", 0)
	VarSetCapacity(sState, (A_IsUnicode?2:1)*nSize)
	DllCall("oleacc\GetStateText", "Uint", nState, "str", sState, "Uint", nSize+1)
	Return	sState
}

; Written by jethrow
Acc_Role(Acc, ChildId=0) {
	try return ComObjType(Acc,"Name")="IAccessible"?Acc_GetRoleText(Acc.accRole(ChildId)):"invalid object"
}
Acc_State(Acc, ChildId=0) {
	try return ComObjType(Acc,"Name")="IAccessible"?Acc_GetStateText(Acc.accState(ChildId)):"invalid object"
}
Acc_Children(Acc) {
	if ComObjType(Acc,"Name")!="IAccessible"
		error_message := "Cause:`tInvalid IAccessible Object`n`n"
	else {
		Acc_Init()
		cChildren:=Acc.accChildCount, Children:=[]
		if DllCall("oleacc\AccessibleChildren", "Ptr", ComObjValue(Acc), "Int", 0, "Int", cChildren, "Ptr", VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*", cChildren)=0 {
			Loop %cChildren%
				i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i), Children.Insert(NumGet(varChildren,i-8)=3?child:Acc_Query(child)), ObjRelease(child)
			return Children
		}
	}
	error:=Exception("",-1)
	MsgBox, 262148, Acc_Children Failed, % (error_message?error_message:"") "File:`t" (error.file==A_ScriptFullPath?A_ScriptName:error.file) "`nLine:`t" error.line "`n`nContinue Script?"
	IfMsgBox, No
		ExitApp
}
Acc_Location(Acc, ChildId=0) { ; adapted from Sean's code
	try Acc.accLocation(ComObj(0x4003,&x:=0), ComObj(0x4003,&y:=0), ComObj(0x4003,&w:=0), ComObj(0x4003,&h:=0), ChildId)
	catch
		return
	return	{x:NumGet(x,0,"int"), y:NumGet(y,0,"int"), w:NumGet(w,0,"int"), h:NumGet(h,0,"int")
		,	pos:"x" NumGet(x,0,"int")" y" NumGet(y,0,"int") " w" NumGet(w,0,"int") " h" NumGet(h,0,"int")}
}
Acc_Parent(Acc) { 
	try parent:=Acc.accParent
	return parent?Acc_Query(parent):
}
Acc_Child(Acc, ChildId=0) {
	try child:=Acc.accChild(ChildId)
	return child?Acc_Query(child):
}
Acc_Query(Acc) { ; thanks Lexikos - www.autohotkey.com/forum/viewtopic.php?t=81731&p=509530#509530
	try return ComObj(9, ComObjQuery(Acc,"{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
}
DRocks
Posts: 525
Joined: 08 May 2018, 10:20

Re: How to get the full ACC path for control on cursor?

30 Sep 2018, 06:36

Ive been looking for this in vain and this is a really nice reponse. Thanks for asking for help and thanks for the complete answer tmplinshi

BTW what I am unable to figure out is how to use this info to Control Focus the given AccPath? This is needed in the example of a custom class of listview that don't report different button names. The only way to identify them is either this method, or image search. I've sticked with image search for the last 3 months but if this retrieved path could then be focused OR get the position of the control, it would be more stable than imagesearch I think.
CyL0N
Posts: 210
Joined: 27 Sep 2018, 09:58

Re: How to get the full ACC path for control on cursor?

01 Oct 2018, 06:34

tmplinshi wrote:The function GetAccPath and GetEnumIndex were extracted from AccViewer.

Code: Select all

#NoEnv
#SingleInstance force
SetBatchLines, -1

F12::
	obj := GetInfoUnderCursor()
	msgbox % obj.path
return

GetInfoUnderCursor() {
	Acc := Acc_ObjectFromPoint(child)
	if !value := Acc.accValue(child)
		value := Acc.accName(child)
	accPath := GetAccPath(acc, hwnd).path
	return {text: value, path: accPath, hwnd: hwnd}
}

GetAccPath(Acc, byref hwnd="") {
	hwnd := Acc_WindowFromObject(Acc)
	WinObj := Acc_ObjectFromWindow(hwnd)
	WinObjPos := Acc_Location(WinObj).pos
	while Acc_WindowFromObject(Parent:=Acc_Parent(Acc)) = hwnd {
		t2 := GetEnumIndex(Acc) "." t2
		if Acc_Location(Parent).pos = WinObjPos
			return {AccObj:Parent, Path:SubStr(t2,1,-1)}
		Acc := Parent
	}
	while Acc_WindowFromObject(Parent:=Acc_Parent(WinObj)) = hwnd
		t1.="P.", WinObj:=Parent
	return {AccObj:Acc, Path:t1 SubStr(t2,1,-1)}
}

GetEnumIndex(Acc, ChildId=0) {
	if Not ChildId {
		ChildPos := Acc_Location(Acc).pos
		For Each, child in Acc_Children(Acc_Parent(Acc))
			if IsObject(child) and Acc_Location(child).pos=ChildPos
				return A_Index
	} 
	else {
		ChildPos := Acc_Location(Acc,ChildId).pos
		For Each, child in Acc_Children(Acc)
			if Not IsObject(child) and Acc_Location(Acc,child).pos=ChildPos
				return A_Index
	}
}

; Acc.ahk

;------------------------------------------------------------------------------
; Acc.ahk Standard Library
; by Sean
; Updated by jethrow:
; 	Modified ComObjEnwrap params from (9,pacc) --> (9,pacc,1)
; 	Changed ComObjUnwrap to ComObjValue in order to avoid AddRef (thanks fincs)
; 	Added Acc_GetRoleText & Acc_GetStateText
; 	Added additional functions - commented below
; 	Removed original Acc_Children function
; last updated 2/19/2012
;------------------------------------------------------------------------------

Acc_Init()
{
	Static	h
	If Not	h
		h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
}
Acc_ObjectFromEvent(ByRef _idChild_, hWnd, idObject, idChild)
{
	Acc_Init()
	If	DllCall("oleacc\AccessibleObjectFromEvent", "Ptr", hWnd, "UInt", idObject, "UInt", idChild, "Ptr*", pacc, "Ptr", VarSetCapacity(varChild,8+2*A_PtrSize,0)*0+&varChild)=0
	Return	ComObjEnwrap(9,pacc,1), _idChild_:=NumGet(varChild,8,"UInt")
}

Acc_ObjectFromPoint(ByRef _idChild_ = "", x = "", y = "")
{
	Acc_Init()
	If	DllCall("oleacc\AccessibleObjectFromPoint", "Int64", x==""||y==""?0*DllCall("GetCursorPos","Int64*",pt)+pt:x&0xFFFFFFFF|y<<32, "Ptr*", pacc, "Ptr", VarSetCapacity(varChild,8+2*A_PtrSize,0)*0+&varChild)=0
	Return	ComObjEnwrap(9,pacc,1), _idChild_:=NumGet(varChild,8,"UInt")
}

Acc_ObjectFromWindow(hWnd, idObject = -4)
{
	Acc_Init()
	If	DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hWnd, "UInt", idObject&=0xFFFFFFFF, "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64"),"Int64"), "Ptr*", pacc)=0
	Return	ComObjEnwrap(9,pacc,1)
}

Acc_WindowFromObject(pacc)
{
	If	DllCall("oleacc\WindowFromAccessibleObject", "Ptr", IsObject(pacc)?ComObjValue(pacc):pacc, "Ptr*", hWnd)=0
	Return	hWnd
}

Acc_GetRoleText(nRole)
{
	nSize := DllCall("oleacc\GetRoleText", "Uint", nRole, "Ptr", 0, "Uint", 0)
	VarSetCapacity(sRole, (A_IsUnicode?2:1)*nSize)
	DllCall("oleacc\GetRoleText", "Uint", nRole, "str", sRole, "Uint", nSize+1)
	Return	sRole
}

Acc_GetStateText(nState)
{
	nSize := DllCall("oleacc\GetStateText", "Uint", nState, "Ptr", 0, "Uint", 0)
	VarSetCapacity(sState, (A_IsUnicode?2:1)*nSize)
	DllCall("oleacc\GetStateText", "Uint", nState, "str", sState, "Uint", nSize+1)
	Return	sState
}

; Written by jethrow
Acc_Role(Acc, ChildId=0) {
	try return ComObjType(Acc,"Name")="IAccessible"?Acc_GetRoleText(Acc.accRole(ChildId)):"invalid object"
}
Acc_State(Acc, ChildId=0) {
	try return ComObjType(Acc,"Name")="IAccessible"?Acc_GetStateText(Acc.accState(ChildId)):"invalid object"
}
Acc_Children(Acc) {
	if ComObjType(Acc,"Name")!="IAccessible"
		error_message := "Cause:`tInvalid IAccessible Object`n`n"
	else {
		Acc_Init()
		cChildren:=Acc.accChildCount, Children:=[]
		if DllCall("oleacc\AccessibleChildren", "Ptr", ComObjValue(Acc), "Int", 0, "Int", cChildren, "Ptr", VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*", cChildren)=0 {
			Loop %cChildren%
				i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i), Children.Insert(NumGet(varChildren,i-8)=3?child:Acc_Query(child)), ObjRelease(child)
			return Children
		}
	}
	error:=Exception("",-1)
	MsgBox, 262148, Acc_Children Failed, % (error_message?error_message:"") "File:`t" (error.file==A_ScriptFullPath?A_ScriptName:error.file) "`nLine:`t" error.line "`n`nContinue Script?"
	IfMsgBox, No
		ExitApp
}
Acc_Location(Acc, ChildId=0) { ; adapted from Sean's code
	try Acc.accLocation(ComObj(0x4003,&x:=0), ComObj(0x4003,&y:=0), ComObj(0x4003,&w:=0), ComObj(0x4003,&h:=0), ChildId)
	catch
		return
	return	{x:NumGet(x,0,"int"), y:NumGet(y,0,"int"), w:NumGet(w,0,"int"), h:NumGet(h,0,"int")
		,	pos:"x" NumGet(x,0,"int")" y" NumGet(y,0,"int") " w" NumGet(w,0,"int") " h" NumGet(h,0,"int")}
}
Acc_Parent(Acc) { 
	try parent:=Acc.accParent
	return parent?Acc_Query(parent):
}
Acc_Child(Acc, ChildId=0) {
	try child:=Acc.accChild(ChildId)
	return child?Acc_Query(child):
}
Acc_Query(Acc) { ; thanks Lexikos - www.autohotkey.com/forum/viewtopic.php?t=81731&p=509530#509530
	try return ComObj(9, ComObjQuery(Acc,"{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
}

Thanks a ton dude.
live ? long & prosper : regards
CyL0N
Posts: 210
Joined: 27 Sep 2018, 09:58

Re: How to get the full ACC path for control on cursor?

01 Oct 2018, 06:35

duplicatePost
Last edited by CyL0N on 01 Oct 2018, 06:46, edited 1 time in total.
live ? long & prosper : regards
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: How to get the full ACC path for control on cursor?

15 Dec 2018, 23:18

- (An acc path is a list of numbers, e.g. '1.2.3'. If you specify an hWnd and '1.2.3', then you start at the GUI element corresponding to the hWnd, you get its 1st child, then that element's 2nd child, then that element's 3rd child, you have then specified a GUI element relative to an hWnd.)
- (So, for an acc path, you start at an hWnd and check various descendants, top-down, as in this script.)
Acc: get text from all window/control elements - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=40615
- Re. starting at a GUI element and discovering its ancestors, until you reach an hWnd, bottom-up. I've investigated the situation, and AFAICS the answer isn't easy, and this may explain why AccViewer isn't perfect in stating the path of an element.
- The accParent property makes it easy to get an element's parent, and you can use this to get information about each ancestor element, producing an Acc path from right-to-left. However, it is not necessarily clear how to determine at which ancestor element you should stop. I.e. which should be considered the first (leftmost) element. Possible criteria: check the hWnd associated with the GUI element, check the role number of the GUI element.
- There is another problem, you start with an element, you get its parent, you then need to know that the child element is the nth child of the parent. AccViewer uses a function called GetEnumIndex to establish this, and it uses the positions of GUI elements to try and establish a child element's number. However, testing demonstrated that this function, and therefore my code below, can sometimes return the wrong number for an element. (But note: if one number is wrong, the other numbers can still be correct, the parent element is always correctly identified, but the child number relative to the parent can be wrong.)

Code: Select all

q:: ;acc get path of element under the cursor (note: may be unreliable, see function comments)
WinGet, hWnd, ID, A
;hWnd := -1 ;get all possible ancestors
oAcc := Acc_ObjectFromPoint(vChildID)
vAccPath := JEE_AccGetPath(oAcc, hWnd)
if vChildID
	MsgBox, % Clipboard := vAccPath " c" vChildID
else
	MsgBox, % Clipboard := vAccPath

;use acc path
oAcc := Acc_Get("Object", vAccPath, vChildID, "ahk_id " hWnd)
vName := vValue := ""
try vName := oAcc.accName(vChildID)
try vValue := oAcc.accValue(vChildID)
MsgBox, % vName "`r`n" vValue
return

;note: path might be too short e.g. menu items
;note: path might be too long (I haven't seen this happen)
;note: path might be wrong (e.g. more than one item with the same position, JEE_AccGetEnumIndex is not guaranteed to give correct results)
JEE_AccGetPath(oAcc, hWnd:="")
{
	local
	if (hWnd = "")
		hWnd := Acc_WindowFromObject(oAcc)
		, hWnd := DllCall("user32\GetParent", Ptr,hWnd, Ptr)
	vAccPath := ""
	vIsMatch := 0
	if (hWnd = -1) ;get all possible ancestors
		Loop
		{
			vIndex := JEE_AccGetEnumIndex(oAcc)
			if !vIndex
				break
			vAccPath := vIndex (A_Index=1?"":".") vAccPath
			oAcc := oAcc.accParent
		}
	else
		Loop
		{
			vIndex := JEE_AccGetEnumIndex(oAcc)
			hWnd2 := Acc_WindowFromObject(oAcc)
			if !vIsMatch && (hWnd = hWnd2)
				vIsMatch := 1
			if vIsMatch && !(hWnd = hWnd2)
				break
			vAccPath := vIndex (A_Index=1?"":".") vAccPath
			if vIsMatch
				break
			oAcc := oAcc.accParent
		}
	return vAccPath
}

;note: AccViewer's GetEnumIndex function uses a mod of Acc_Location that returns a 'pos' key
JEE_AccGetEnumIndex(oAcc, vChildID:=0)
{
	local
	if !vChildID
	{
		Acc_Location(oAcc, 0, vChildPos)
		for _, oChild in Acc_Children(Acc_Parent(oAcc))
		{
			Acc_Location(oChild, 0, vPos)
			if IsObject(oChild) && (vPos = vChildPos)
				return A_Index
		}
	}
	else
	{
		Acc_Location(oAcc, vChildID, vChildPos)
		for _, oChild in Acc_Children(oAcc)
		{
			Acc_Location(oAcc, oChild, vPos)
			if !IsObject(oChild) && (vPos = vChildPos)
				return A_Index
		}
	}
}
- One other thing to mention is that the Acc library's Acc_Location function returns an object with x/y/w/h keys. However, the Acc_Location function used by AccViewer has been modified to also return a pos key. I wrote my code to be more compatible, it assumes that the pos key is not available. Cheers.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: How to get the full ACC path for control on cursor?

26 Dec 2018, 12:34

- I've updated both functions. The two issues resolved were: get the right numbers (if there is ambiguity, all possible indexes are returned, separated by 'or'), get the right number of numbers (it worked for all of the tests I did).
- Do notify of any issues. Thanks.

Code: Select all

q:: ;acc get path of element under the cursor
WinGet, hWnd, ID, A
;hWnd := -1 ;get all possible ancestors
oAcc := Acc_ObjectFromPoint(vChildID)
vAccPath := JEE_AccGetPath(oAcc, hWnd)
if vChildID
	MsgBox, % Clipboard := vAccPath " c" vChildID
else
	MsgBox, % Clipboard := vAccPath

;use acc path (check that the acc path is correct)
oAcc := Acc_Get("Object", vAccPath, vChildID, "ahk_id " hWnd)
vName := vValue := ""
try vName := oAcc.accName(vChildID)
try vValue := oAcc.accValue(vChildID)
MsgBox, % vName "`r`n" vValue
return

;==================================================

;note: path might be too short e.g. menu items
;note: path might be too long (I haven't seen this happen)
;note: if there is ambiguity identifying the index, 'or' is used (see JEE_AccGetEnumIndex)
JEE_AccGetPath(oAcc, hWnd:="")
{
	local
	if (hWnd = "")
		hWnd := Acc_WindowFromObject(oAcc)
		, hWnd := DllCall("user32\GetParent", Ptr,hWnd, Ptr)
	vAccPath := ""
	vIsMatch := 0
	if (hWnd = -1) ;get all possible ancestors
		Loop
		{
			vIndex := JEE_AccGetEnumIndex(oAcc)
			if !vIndex
				break
			vAccPath := vIndex (A_Index=1?"":".") vAccPath
			oAcc := oAcc.accParent
		}
	else
		Loop
		{
			vIndex := JEE_AccGetEnumIndex(oAcc)
			hWnd2 := Acc_WindowFromObject(oAcc)
			if !vIsMatch && (hWnd = hWnd2)
				vIsMatch := 1
			if vIsMatch && !(hWnd = hWnd2)
				break
			vAccPath := vIndex (A_Index=1?"":".") vAccPath
			oAcc := oAcc.accParent
		}
	if vIsMatch
		return SubStr(vAccPath, InStr(vAccPath, ".")+1)
	return vAccPath
}

;==================================================

;note: AccViewer uses a mod of Acc_Location that returns a 'pos' key,
;this function is compatible with the older and AccViewer versions of Acc_Location
;note: if there is ambiguity identifying the index, 'or' is used
JEE_AccGetEnumIndex(oAcc, vChildID:=0)
{
	local
	vOutput := ""
	vAccState := oAcc.accState(0)
	if !vChildID
	{
		Acc_Location(oAcc, 0, vChildPos)
		for _, oChild in Acc_Children(Acc_Parent(oAcc))
		{
			if !(vAccState = oChild.accState(0))
				continue
			Acc_Location(oChild, 0, vPos)
			if IsObject(oChild) && (vPos = vChildPos)
				vOutput .= A_Index "or"
		}
	}
	else
	{
		Acc_Location(oAcc, vChildID, vChildPos)
		for _, oChild in Acc_Children(oAcc)
		{
			if !(vAccState = oChild.accState(0))
				continue
			Acc_Location(oAcc, oChild, vPos)
			if !IsObject(oChild) && (vPos = vChildPos)
				vOutput .= A_Index "or"
		}
	}
	return SubStr(vOutput, 1, -2)
}

;==================================================
- To get the right numbers (indexes), you must compare the GUI element against itself and all of its siblings. You need some unique info. The function already checked the element locations, and it now checks the element states (e.g. STATE_SYSTEM_INVISIBLE := 0x8000). If it is still unable to distinguish the correct element, it separates candidate numbers with 'or' e.g. '1.2or3.4'. (It's possible that other identifying info could also be used to confirm the right element.)
- To get the right number of numbers, I improved the function based on the test below, e.g. I used it on a Notepad menubar item and Edit control. You start at the GUI element, repeatedly getting the next parent element, you continue until you reach a GUI element with the hWnd that you specified, and you then continue until you reach a GUI element with a different hWnd. Cheers.

Code: Select all

q:: ;acc get info for element under the cursor and ancestors
WinGet, hWnd, ID, A
oAcc := Acc_ObjectFromPoint()
vOutput := ""
Loop, 10
{
	vOutput .= Acc_WindowFromObject(oAcc) "|" Format("0x{:X}", oAcc.accRole(0)) "|" RegExReplace(oAcc.accName(0), "s)[`r`n].*", "...") "|" RegExReplace(oAcc.accValue(0), "s)[`r`n].*", "...") "`r`n"
	oAcc := oAcc.accParent
}
Clipboard := vOutput
MsgBox, % vOutput
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “Ask For Help”

Who is online

Users browsing this forum: Bing [Bot], boiler, Odlanir, pn4265 and 226 guests