Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Acc Library [AHK_L] (updated 09/27/2012)


  • Please log in to reply
49 replies to this topic
kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
I have another question, some programs like Firefox don't allow detection of caret position, can we use ACC to detect caret position in any window?

sinkfaze
  • Moderators
  • 6367 posts
  • Last active: Nov 30 2018 08:50 PM
  • Joined: 18 Mar 2008
FINALLY I got it to invoke the 'Open...' Menu in Notepad:

if	!window :=	Acc_Parent(Acc_ObjectFromWindow(WinExist("ahk_class Notepad")))
{
	MsgBox, Epic FAIL d00d.
	return
}
For each, object in Acc_Children(window)
{}	Until	(object.accName="Application")
Sleep, 100
For each, mainMenu in Acc_Children(object)
{}	Until	(mainMenu.accName="File")
WinGet, b, List, ahk_class #32768
mainMenu.accDoDefaultAction(1), a :=	b
Sleep, 50
While	(a=b)
	WinGet, a, List, ahk_class #32768
fileMenu :=	Acc_ObjectFromWindow(a%a%)
For each, item in Acc_Children(fileMenu)
{}	Until	InStr(fileMenu.accName(item),"Open")
fileMenu.accDoDefaultAction(item)

And once I got this working I tried going the "silent" route and found I was correct; you have to invoke the menu item window in order for the menu item to execute.

What confuses me is the need to add the Sleep. Not in the case of the Sleep after accDoDefaultAction, since the menu window needs a moment to appear. But if I don't have that Sleep, 100 after the first For-loop, the code will never execute. I only caught it because I was manually adding message boxes at every step just to verify I had the right object, and when I added the message box after that loop and dismissed it, everything suddenly worked just like it was supposed to. It's as if something hasn't caught up yet.

EDIT: A couple of menu item helper functions for your consideration in the library, will greatly ease code bloat when trying to find the newest menu window when navigating through multiple menus:

Acc_getMenus() {

	obj :=	{}
	WinGet, menu, List, ahk_class #32768
	Loop %	menu
		obj[menu%A_Index%] :=	1
	return	obj
}

Acc_getNewMenu(obj) {

	new :=	obj.MaxIndex()
	While	(new=obj.MaxIndex())
		WinGet, new, List, ahk_class #32768
	Loop %	new
		if	!obj[new%A_Index%]
			return	new%A_Index%
}

Example usage:

For each, mainMenu in Acc_Children(object)
{}	Until	(mainMenu.accName="File")
win :=	Acc_getMenus(), mainMenu.accDoDefaultAction(1)
Sleep, 50
fileMenu :=	Acc_ObjectFromWindow(Acc_getNewMenu(win))

EDIT: Modified object creation in Acc_getMenus() function per Lexikos' advice.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

obj.Insert(menu%A_Index%,1)

When you do this, any previously inserted handles with values greater than menu%A_Index% will be incremented by 1, thus corrupting them. Use obj[menu%A_Index%] := 1 instead if you really want to use the handles as keys vs values.

rbrtryn
  • Members
  • 1177 posts
  • Last active: Sep 11 2013 08:04 PM
  • Joined: 22 Jun 2011
It would be helpful if there was some kind of documentation for this library.

My Scripts are written for the latest released version of AutoHotkey.

Need a secure, accessible place to backup your stuff? Use Dropbox!


sinkfaze
  • Moderators
  • 6367 posts
  • Last active: Nov 30 2018 08:50 PM
  • Joined: 18 Mar 2008
I'm trying to use the functions to "click" on a radio button in a certain piece of software, but I can't get it working entirely. When I execute the code the button is marked as per the default action, but the associated event with that radio button is not fired. Any ideas on what I should be looking for to get it working?

boli
  • Guests
  • Last active:
  • Joined: --
The download link https://ahknet.autoh...jethrow/Acc.ahk gives a 404 error.

sinkfaze
  • Moderators
  • 6367 posts
  • Last active: Nov 30 2018 08:50 PM
  • Joined: 18 Mar 2008
That's because the download link no longer exists (see here), and unfortunately jethrow has been off site for about the past week, so he probably doesn't even know it happened.

Here's my copy of the library file:

;------------------------------------------------------------------------------
; 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) {
	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, 262420, Acc_Children Failed, % "File:  " error.file "`nLine: " 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)
}


Pulover
  • Members
  • 1596 posts
  • Last active: Apr 06 2016 04:00 AM
  • Joined: 20 Apr 2012

I have another question, some programs like Firefox don't allow detection of caret position, can we use ACC to detect caret position in any window?

I am also very interested in this possibility. I noticed two entries in the Accessibility constants:

OBJID_CARET = 0xFFFFFFF8
ROLE_SYSTEM_CARET = 0x00000007

I've tried a few things but as of now I have no idea what function to use or how.
Can anybody please give me an example?

Rodolfo U. Batista
Pulover's Macro Creator - Automation Tool (Recorder & Script Writer) | Class_LV_Rows - Copy, Cut, Paste and Drag ListViews | Class_Toolbar - Create and modify | Class_Rebar - Adjustable GUI controls

Join the New AutoHotkey Forum!


jethrow
  • Moderators
  • 2854 posts
  • Last active: May 17 2017 01:57 AM
  • Joined: 24 May 2009

I have another question, some programs like Firefox don't allow detection of caret position ...

I am also very interested in this possibility ... Can anybody please give me an example?

See - Firefox Bug 574672 - Caret object unavailable until focus change. Workaround in red, which also causes A_CaretX/Y to work:
SetTitleMatchMode, 2
OBJID_CARET := 0xFFFFFFF8

F1::
WinGet, hwnd, ID, Firefox

[color=#FF0000]WinGetPos, x, y, w, h, ahk_id %hwnd%
Loop 2 {[/color]
	Acc_Caret := Acc_ObjectFromWindow(hwnd, OBJID_CARET)
	Caret_Location := Acc_Location(Acc_Caret)
	[color=#FF0000]if (Caret_Location.pos != "x0 y0 w0 h0") or (A_Cursor != "IBeam")
		break
	else {
		Acc_ObjectFromPoint(child, (x+w)/2, (y+h)/2)
		ControlSend, ahk_parent, {tab}, ahk_id %hwnd%
		Sleep 1000
		ControlSend, ahk_parent, {shift down}{tab}, ahk_id %hwnd%
		Sleep 250
	}
}[/color]

for k,v in Caret_Location
   output .= k " =`t" v "`n"
   
WinGetTitle, title, ahk_id %hwnd%
ToolTip % output "`nA_CaretX = " A_CaretX+x "`nA_CaretY = " A_CaretY+y
output := ""

QUESTION: For the Acc_WindowFromObject function, Sean had used the client for the default ObjectId parameter. Can anyone think of a good reason why the default should't be the Window/Self ObjectId? This would seem more intuitive to me ...

Pulover
  • Members
  • 1596 posts
  • Last active: Apr 06 2016 04:00 AM
  • Joined: 20 Apr 2012
It's working great on Firefox! Thank you very much, jethrow!
It solves a problem with my project. I couldn't get it to work on Thunderbird, though... is it possible?
Thanks again! :)

Rodolfo U. Batista
Pulover's Macro Creator - Automation Tool (Recorder & Script Writer) | Class_LV_Rows - Copy, Cut, Paste and Drag ListViews | Class_Toolbar - Create and modify | Class_Rebar - Adjustable GUI controls

Join the New AutoHotkey Forum!


GodlyCheese
  • Members
  • 719 posts
  • Last active: Nov 11 2014 07:12 PM
  • Joined: 30 Aug 2012
http://puu.sh/13luS

When trying to run the version of Acc.ahk in your first post. Same thing happens when I use #Include for it in another script. Unfortunately I'm not familiar enough with your library to make any sense out of the error message. I suspect that I'm missing some .dll file? However not sure if that's correct, and not sure which.

sinkfaze
  • Moderators
  • 6367 posts
  • Last active: Nov 30 2018 08:50 PM
  • Joined: 18 Mar 2008

I couldn't get it to work on Thunderbird, though...


More specifically, I'm not able to obtain Thunderbird's menu windows. Have you experimented with Thunderbird at all jethrow?

jethrow
  • Moderators
  • 2854 posts
  • Last active: May 17 2017 01:57 AM
  • Joined: 24 May 2009

Have you experimented with Thunderbird at all jethrow?

No

I'm working on implementing functions that will allow the user to get information from the screen almost as easily as ControlGetText. The ChildPath needed will be obtained using Accessible Info Viewer, which I'm planning on updating sometime. (currently it accesses the client object, rather than the window object, to start the path). Here is an example of getting the url in Firefox v15:
SetTitleMatchMode 2
[color=#008000]/* Acc_Get(Cmd, ChildPath="", ChildID=0, WinTitle="", WinText="", ExcludeTitle="", ExcludeText="")
	RunTime errors can be handled with ErrorLevel, or by turning on the Acc Errors via Acc_Error(true)
*/[/color]

MsgBox % Acc_Get("Value", "4.19.2.4.2", 0, "Firefox")
[color=#008000]/* AHK v2 command syntax:
	Acc_Get, FF_Url, Value, 4.19.2.4.2, 0, Firefox
	MsgBox %FF_Url%
*/[/color]

_path =
(join.
application
property page
tool bar2
combo box
editable text
) 
[color=#008000]/* alertnative: can use _ instead of space; no number = 1
	_path = application1.property_page1.tool_bar2.combo_box1.editable_text1
*/[/color]

MsgBox % Acc_Get("Value", _path, 0, "ahk_class MozillaWindowClass")

I'm interested in any input ... good idea, bad idea, suggestions ...

Acc_Error(p="") {
	static setting:=0
	return p=""?setting:setting:=p
}
Acc_Children(Acc) {
	if ComObjType(Acc,"Name") != "IAccessible"
		ErrorLevel := "Invalid IAccessible Object"
	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)=9?Acc_Query(child):child), NumGet(varChildren,i-8)=9?ObjRelease(child):
			return Children.MaxIndex()?Children:
		} else
			ErrorLevel := "AccessibleChildren DllCall Failed"
	}
	if Acc_Error()
		throw Exception(ErrorLevel,-1)
}
Acc_ChildrenByRole(Acc, Role) {
	if ComObjType(Acc,"Name")!="IAccessible"
		ErrorLevel := "Invalid IAccessible Object"
	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)
				if NumGet(varChildren,i-8)=9
					AccChild:=Acc_Query(child), ObjRelease(child), Acc_Role(AccChild)=Role?Children.Insert(AccChild):
				else
					Acc_Role(Acc, child)=Role?Children.Insert(child):
			}
			return Children.MaxIndex()?Children:, ErrorLevel:=0
		} else
			ErrorLevel := "AccessibleChildren DllCall Failed"
	}
	if Acc_Error()
		throw Exception(ErrorLevel,-1)
}
Acc_Get(Cmd, ChildPath="", ChildID=0, WinTitle="", WinText="", ExcludeTitle="", ExcludeText="") {
	AccObj :=   IsObject(WinTitle)? WinTitle
			:   Acc_ObjectFromWindow( WinExist(WinTitle, WinText, ExcludeTitle, ExcludeText), 0 )
	if ComObjType(AccObj, "Name") != "IAccessible"
		ErrorLevel := "Could not access an IAccessible Object"
	else {
		StringReplace, ChildPath, ChildPath, _, %A_Space%, All
		AccError:=Acc_Error(), Acc_Error(true)
		Loop Parse, ChildPath, ., %A_Space%
			try {
				if A_LoopField is digit
					Children:=Acc_Children(AccObj), m2:=A_LoopField ; mimic "m2" output in else-statement regex
				else
					RegExMatch(A_LoopField, "(\D*)(\d*)", m), Children:=Acc_ChildrenByRole(AccObj, m1), m2:=(m2?m2:1)
				if Not Children.HasKey(m2)
					throw
				AccObj := Children[m2]
			} catch {
				ErrorLevel:="Cannot access ChildPath Item #" A_Index " -> " A_LoopField, Acc_Error(AccError)
				if Acc_Error()
					throw Exception("Cannot access ChildPath Item", -1, "Item #" A_Index " -> " A_LoopField)
				return
			}
		Acc_Error(AccError)
		try ret_val := AccObj["acc" Cmd](ChildID+0)
		catch {
			ErrorLevel := """" Cmd """ Command Not Implemented"
			if Acc_Error()
				throw Exception("Command Not Implemented", -1, Cmd)
			return
		}
		return ret_val, ErrorLevel:=0
	}
	if Acc_Error()
		throw Exception(ErrorLevel,-1)
}


Marcelo NS
  • Members
  • 8 posts
  • Last active: Feb 23 2013 08:29 PM
  • Joined: 24 Jan 2013

I have a problem with Acc_ObjectFromWindow. I was working on a horizontal scrolling script. It works as expected in Word and Excel, however in PPT only scrolls to the right and never to the left. 

I have tried replacing the variables by hardcoding the classes and text but no luck, any idea?

;Define Matching Method
SetTitleMatchMode, RegEx

;For MS Office
#IfWinActive ahk_class (PPT|OpusApp|XLMAIN)
WheelLeft:: 
WheelRight:: 
	Acc_ObjectFromWindow(ActiveWinClassNN(), -16).SmallScroll(0,0,InStr(A_ThisHotkey,"Left")? -1:1)
return
#IfWinActive

ActiveWinClassNN() 
{
	MouseGetPos, , , id, class, 2
	return class
}


Philister
  • Members
  • 57 posts
  • Last active: Sep 27 2016 08:10 PM
  • Joined: 12 Feb 2012

Asked a question about an error message related to the Acc Library in the below post. Maybe someone following this thread here can help? (Hope my little cross-post doesn't annoy anyone...)

 

http://www.autohotke...-link/?p=567672
 

Thanks.