AccV2

Post your working scripts, libraries and tools for AHK v1.1 and older
sancarn
Posts: 224
Joined: 01 Mar 2016, 14:52

AccV2

26 Nov 2017, 11:35

This code was created initially by Sean, however since then there have been updates by Jethrow and myself. Most of my updates have been private so far, but figured they'd be useful for others. So I decided to open source the code on Github.

Bare in mind that I am planning on changing the library into a fully class based system. But at the moment I'm just trying to get a current project done! :P

Changes by myself:

* Added a bunch of enumerations. E.G. ACC_STATE.SELECTED, ACC_ROLE.WINDOW,...

* Added IAccessible walking functionality e.g.

Code: Select all

   acc_childrenFilter(oAcc, ACC_FILTERS.byDescription, "Amazing button")
   
   acc_childrenFilter(oAcc, Func("myAwesomeFunction"), true)
   myAwesomeFunction(oAcc,val){
       return val
   }
* Modified Acc_Children function and added ACC_ChildProxy class. If accChild, in ACC_Children, returns a VT_I4 value, ACC_ChildProxy is used instead of IAccessible. If anyone knows how to get an IAccessible interface from a child ID like this, I'm all ears!

* Added acc_childrenFilter. This should be intergrated into Acc_Children() like Acc_Children(oAcc,ACC_FILTER.byName, "CoolButton",true,ACC_FILTER)
Last edited by sancarn on 30 Nov 2017, 06:09, edited 1 time in total.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Acc

26 Nov 2017, 12:53

Hello sancarn, I have my own small wish list of changes to Acc, or rather AccViewer that I will try implement at some point. Every time other people have issues with Acc, I never quite appreciate what they have in mind to do. I would be very interested to hear more detail about what changes you've made, and why they are useful to you. Imagine explaining it to a complete newbie. Thanks.

Btw if you use AccViewer, you can see that elements can have two types of children, one is sort of an object that can have children, listed in bold, and one isn't. I don't have anything more specific to say about that.

Two links, in case there is any useful information there.
Acc library (MSAA) and AccViewer download links - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=26201
AccViewer Basic - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=32039
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Himmilayah

Re: Acc

27 Nov 2017, 02:48

wondering if any of you would know how to get the data that acc viewer shows in the accessible part (lower part) of the program called Accessible Info.

What is the code for me to write if I want to get the Name data that ACC shows in the accessible info?

I searched for so long and had no success in getting a solution.

My thinking is, if ACC can see it, I should be able to create some code that goes to a program and generates a list of all accessible info.

Anyone can help please?
sancarn
Posts: 224
Joined: 01 Mar 2016, 14:52

Re: Acc

27 Nov 2017, 03:37

jeeswg wrote:I would be very interested to hear more detail about what changes you've made
There are a few reasons why I added the things I have added.

1. Current functionality didn't include certain features. E.G. Getting the currently selected item required a special state constant which wasn't in the initial acc.ahk (hence why I added all relevant enumerations) and also wasn't covered by acc_stateText().

2. Sometimes acc_children returned child IDs instead of IAccessible objects. This makes programming with them difficult as there is no consistency! You can still access these properties like accParent.accProperty(childID). The child proxy class does this to fill the class, which at least brings some consistency.

3. The functions for walking the tree given a certain condition is just really handy... It replaces acc_childrenByRole and acc_childrenByName with a more versatile system.
jeeswg wrote:Btw if you use AccViewer, you can see that elements can have two types of children, one is sort of an object that can have children, listed in bold, and one isn't.
Very interesting! I will need that myself for acc_descendents when I finally make that.
sancarn
Posts: 224
Joined: 01 Mar 2016, 14:52

Re: Acc

27 Nov 2017, 11:53

Himmilayah wrote:wondering if any of you would know how to get the data that acc viewer shows in the accessible part (lower part) of the program called Accessible Info.

What is the code for me to write if I want to get the Name data that ACC shows in the accessible info??
The acc code returns an object which implements the IAccessible interface. Googling "IAccessible interface" gives you this link.

As you can see, oAcc.accName will return the name of the item, oAcc.accValue will return the value etc. To get the role and state text you can use Acc_GetRoleText(oAcc.accRole) and Acc_GetStateText(oAcc.accState). However personally I'd advise you stay away from these and test explicitly for what you want. E.G.

Code: Select all

if oAcc.accRole = ACC_ROLE.WINDOW
   msgbox, window
else
   msgbox, other
   
if oAcc.accState & ACC_STATE.SELECTED
   msgbox, selected
else
   msgbox, not selected
Himmilayah

Re: Acc

27 Nov 2017, 14:45

sancarn wrote:
Himmilayah wrote:wondering if any of you would know how to get the data that acc viewer shows in the accessible part (lower part) of the program called Accessible Info.

What is the code for me to write if I want to get the Name data that ACC shows in the accessible info??
The acc code returns an object which implements the IAccessible interface. Googling "IAccessible interface" gives you this link.

As you can see, oAcc.accName will return the name of the item, oAcc.accValue will return the value etc. To get the role and state text you can use Acc_GetRoleText(oAcc.accRole) and Acc_GetStateText(oAcc.accState). However personally I'd advise you stay away from these and test explicitly for what you want. E.G.

Code: Select all

if oAcc.accRole = ACC_ROLE.WINDOW
   msgbox, window
else
   msgbox, other
   
if oAcc.accState & ACC_STATE.SELECTED
   msgbox, selected
else
   msgbox, not selected
Thanks, so for example, can I have my script go to a window and bring me back just the name content of one control?

Any pointers on how to do this in a script? I know how to get the text of controls etc but not the accessible info type text (under name in acc viewer)
sancarn
Posts: 224
Joined: 01 Mar 2016, 14:52

Re: Acc

27 Nov 2017, 18:53

Thanks, so for example, can I have my script go to a window and bring me back just the name content of one control?
Sure in numerous ways. E.G. for notepad.exe (untested):

Code: Select all

window    := acc_ObjectFromWindow(winexist("ahk_exe notepad.exe"))
menuBar   := acc_childrenFilter(window,ACC_FILTERS.byRole,ACC_ROLE.MENUBAR,true)
menuItems := acc_children(menuBar)
Loop, % menuItems.length()
{
    menuItem := menuItems[A_Index]
    msgbox, % menuItem.accName . ";" . menuItem.accChildCount
}
Himmilayah

Re: Acc

27 Nov 2017, 19:24

sancarn wrote:
Thanks, so for example, can I have my script go to a window and bring me back just the name content of one control?
Sure in numerous ways. E.G. for notepad.exe (untested):

Code: Select all

window    := acc_ObjectFromWindow(winexist("ahk_exe notepad.exe"))
menuBar   := acc_childrenFilter(window,ACC_FILTERS.byRole,ACC_ROLE.MENUBAR,true)
menuItems := acc_children(menuBar)
Loop, % menuItems.length()
{
    menuItem := menuItems[A_Index]
    msgbox, % menuItem.accName . ";" . menuItem.accChildCount
}
thank you!
I tried the following script

Code: Select all

#Include acc.ahk

window    := acc_ObjectFromWindow(winexist("ahk_exe notepad.exe"))
menuBar   := acc_childrenFilter(window,ACC_FILTERS.byRole,ACC_ROLE.MENUBAR,true)
menuItems := acc_children(menuBar)
Loop, % menuItems.length()
{
    menuItem := menuItems[A_Index]
    msgbox, % menuItem.accName . ";" . menuItem.accChildCount
}
but got error: ==> Duplicate function definition.
Specifically: Acc_Init()


what am I doing wrong? :headwall:
Himmilayah

Re: Acc

27 Nov 2017, 19:39

ok, I had the wrong acc.ahk, now I get no errors but the message box does not popup with any info (it doesnt popup at all)
sancarn
Posts: 224
Joined: 01 Mar 2016, 14:52

Re: Acc

27 Nov 2017, 20:26

Himmilayah wrote:ok, I had the wrong acc.ahk, now I get no errors but the message box does not popup with any info (it doesnt popup at all)
There were a few silly mistakes, after looking in the viewer, I figured out the issues:

Code: Select all

#Include acc.ahk

window    := acc_ObjectFromWindow(winexist("ahk_exe notepad.exe"),ACC_OBJID.WINDOW)
menuBar   := acc_childrenFilter(window,ACC_FILTERS.byName,"Application",true)
menuItems := acc_children(menuBar)
Loop, % menuItems.length()
{
    menuItem := menuItems[A_Index]
    msgbox, % menuItem.accName . ";" . menuItem.accChildCount
}
Note I could have also used menuBar := acc_childrenFilter(window,ACC_FILTERS.byRole,ACC_ROLE.MENUBAR)[2] however it is often not a good idea to rely on child indexes like this. Makes less manageable applications.
Himmilayah

Re: Acc

27 Nov 2017, 21:17

sancarn wrote:
Himmilayah wrote:ok, I had the wrong acc.ahk, now I get no errors but the message box does not popup with any info (it doesnt popup at all)
There were a few silly mistakes, after looking in the viewer, I figured out the issues:

Code: Select all

#Include acc.ahk

window    := acc_ObjectFromWindow(winexist("ahk_exe notepad.exe"),ACC_OBJID.WINDOW)
menuBar   := acc_childrenFilter(window,ACC_FILTERS.byName,"Application",true)
menuItems := acc_children(menuBar)
Loop, % menuItems.length()
{
    menuItem := menuItems[A_Index]
    msgbox, % menuItem.accName . ";" . menuItem.accChildCount
}
Note I could have also used menuBar := acc_childrenFilter(window,ACC_FILTERS.byRole,ACC_ROLE.MENUBAR)[2] however it is often not a good idea to rely on child indexes like this. Makes less manageable applications.
that worked, it gave me the menu items quick and it matches what I see in the accessible info in acc viewer

my end goal is to build something that when I give it the name of the program ahk_exe... it returns to me ALL the accessible info of the name field of that program.

So far I had no luck funding anything like that
Himmilayah

Re: Acc

28 Nov 2017, 03:09

sancarn wrote:
Himmilayah wrote:ok, I had the wrong acc.ahk, now I get no errors but the message box does not popup with any info (it doesnt popup at all)
There were a few silly mistakes, after looking in the viewer, I figured out the issues:

Code: Select all

#Include acc.ahk

window    := acc_ObjectFromWindow(winexist("ahk_exe notepad.exe"),ACC_OBJID.WINDOW)
menuBar   := acc_childrenFilter(window,ACC_FILTERS.byName,"Application",true)
menuItems := acc_children(menuBar)
Loop, % menuItems.length()
{
    menuItem := menuItems[A_Index]
    msgbox, % menuItem.accName . ";" . menuItem.accChildCount
}
Note I could have also used menuBar := acc_childrenFilter(window,ACC_FILTERS.byRole,ACC_ROLE.MENUBAR)[2] however it is often not a good idea to rely on child indexes like this. Makes less manageable applications.
I have a program that has text on it that acc viewer can see (under accessible name)

I took your code tried to modify it based on what I saw in the notes on top of acc.ahk plus on the msdn site.

Is this a feature that is missing acc_ObjectFromClient?

The program I am using has many areas that displays text, they are not areas I enter text, I tried the following 2 ways but it doesnt give me the data (no error)

Code: Select all

#Include acc.ahk

client    := acc_ObjectFromWindow(winexist("ahk_exe Tradeauto.exe"),ACC_OBJID.CLIENT)
STATICTEXT   := acc_childrenFilter(client,ACC_FILTERS.byName,"Log",true)
data1 := acc_children(STATICTEXT)
Loop, % data1.length()
{
    STATICTEXT := data1[A_Index]
    msgbox, % STATICTEXT.accName . ";" . STATICTEXT.accChildCount
}
I also tried a few other things, like instead of Statictext I just used text,

I need some help if possible, how do I write this code so I can get the text? there has to be a way because acc viewer gets it

Thank you for any help!
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Acc

28 Nov 2017, 03:57

I've posted some initial content, i.e. links to various examples that use Acc_Get here:
jeeswg's Acc tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=40590

I will have a look, during the next few days, at retrieving information/text from the descendants of an Acc object.
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: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Acc

28 Nov 2017, 17:02

OK, I managed to put some code together.
Acc: get text from all window/control elements - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=40615
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
sancarn
Posts: 224
Joined: 01 Mar 2016, 14:52

Re: Acc

30 Nov 2017, 06:07

I guess I should rename the library given that this Acc.ahk will be incompatible with old Acc.ahk scripts.
sancarn
Posts: 224
Joined: 01 Mar 2016, 14:52

Re: Acc

02 Dec 2017, 06:26

Himmilayah wrote: Is this a feature that is missing acc_ObjectFromClient?
Nope. Client is an IAccessible Object so Object from Client makes no sense...
Himmilayah wrote: The program I am using has many areas that displays text, they are not areas I enter text, I tried the following 2 ways but it doesnt give me the data (no error)

I also tried a few other things, like instead of Statictext I just used text,

I need some help if possible, how do I write this code so I can get the text? there has to be a way because acc viewer gets it
Normally when Acc stuff hasn't worked for me in the past it's been because I overlooked something. Like e.g. a menu bar having the same name as the window I am wanting to access. So for example: STATICTEXT := acc_childrenFilter(client,ACC_FILTERS.byName,"Log",true) will only work if the "Log" element is the first child in client with name "Log". Otherwise you would want to use code like:

Code: Select all

STATICTEXT   := acc_childrenFilter(client,ACC_FILTERS.byName,"Log")
Loop, % STATICTEXT.length
{
    if STATICTEXT[A_Index].accRole = ACC_ROLE.WINDOW
        break
}
or better still

Code: Select all

STATICTEXT   := acc_childrenFilter(client,Func("logFilter"),"Log",true)
...

logFilter(oObj,val){
    return oObj.accName = "Log" && oObj.accRole = ACC_ROLE.WINDOW
}
Another possibility are that you should be using accValue or accDescription etc.

However in general one problem with Acc is you have to follow a very specific structure, and it's very easy to think you're in the right place when you really are not.
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: AccV2

03 Dec 2017, 10:46

anyway to keep this library backward compatible with old acc scripts? maybe you and jeeswg can work together?

sancarn
Posts: 224
Joined: 01 Mar 2016, 14:52

Re: AccV2

05 Dec 2017, 09:05

guest3456 wrote:anyway to keep this library backward compatible with old acc scripts? maybe you and jeeswg can work together?
Although I have no issue with working with Jeeswg, I would have an issue with backwards compatibility. I agree that backwards compatibility is great to have but i personally do dislike the convention prefix_funcName(Obj,args) compared to obj.funcName(args). I'm not sure I'd want these names polluting the global namespace unless there is a way to enforce it optionally.

If you could do:

Code: Select all

;Base code here...

#if ACC_BACKWARDS_COMPATABLE

acc_children(obj){

}
that'd be great. But sadly you can't (as per my previous tests at least).

I guess it would be easy to implement those features as a separate file in the repo though. So will look into that :)
Last edited by sancarn on 06 Dec 2017, 09:02, edited 1 time in total.
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: AccV2

05 Dec 2017, 14:11

ahh gotcha ok

just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AccV2

21 Dec 2017, 10:42

Hi sancarn,

in 2015 I started to rewrite the Acc Library, mainly to understand how this stuff works. For this reason and because I noticed problems with some windows I switched back to direct interface calls which give a bit more control. Because I noticed that accessible objects are identified by their interface pointer as well as their child ID I decided to use an AHK object wrapping the accessibility features, similar as you did with your child proxy. But at some point I lost interest and stopped the work.

Because of this topic and because I had the time I tried to complete my scripts in regard to the basic features. I'm still not interested enough to do further development, but I want to share what I have so far. Maybe you or someone else is interested in using (parts) of the code and will save some time. It can be used without any restrictions or conditions. I tested the code to some degree and hopefully found and fixed the most bugs.

My "AccObj Library" contains five scripts
  1. AccObj_Object.ahk
    The class definition for the AHK object wrapping the IAccessible properties and method and some of the accessibility client functions.
  2. AccObj_Functions.ahk
    Wrappers for some accessibility client functions and some additional functions, not including any event handling functions.
  3. AccObj_Constants.ahk
    Constants (defined as classes) for values used with active accessibility.
  4. AccObj_Lib.ahk
    Contains only three #Include commands for the above scripts.
  5. AccObj_Viewer.ahk
    A rewrite of the "Accessible Info Viewer" as a 'how-to-use' example. Only (imo) mandatory features have been implemented.
Source Code

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 208 guests