来源说明:本文根据 AutoHotkey 论坛 jeeswg 的 jeeswg's Acc tutorial (Microsoft Active Accessibility) (MSAA) 整理翻译,并结合 AHK66 站内已有 Acc、UIA、控件自动化内容重新组织。原帖本身更像 Acc 资料索引和问答集合,本文不会机械搬运一长串跳转链接,而是把最适合上手的概念、写法和排查路线整理成一篇实用入门。
Acc 是 Microsoft Active Accessibility,也常被称为 MSAA。它原本是 Windows 辅助功能体系的一部分,用来让读屏软件、自动化工具读取窗口、控件和界面元素的信息。放到 AHK 里看,Acc 的价值很直接:当 Window Spy、ControlGetText、ControlClick 这些普通控件方法拿不到内容时,Acc 往往还能读到元素名称、值、状态、位置,甚至触发默认动作。
但 Acc 不是万能钥匙。我的建议是:标准控件先用 Control 系列;非标准但还能暴露 MSAA 信息的界面,再用 Acc;更现代的 Chromium、WPF、UWP、Electron 或复杂软件界面,再考虑 UIA、浏览器控制、图色识别等路线。站内已有 AHK 自动化到底该用哪种方法、控件自动化入门、UIA 窗口控制辅助增强库,这篇主要补上 Acc/MSAA 的思路。
一、什么时候值得用 Acc
下面这些情况比较适合尝试 Acc:
- 窗口里看得到文字,但
WinGetText或ControlGetText拿不到。 - 按钮、选项卡、列表项没有稳定的 ClassNN,普通控件命令不好操作。
- 想判断某个元素是否聚焦、选中、隐藏、可点击。
- 想获取鼠标下元素的名称、值、位置,然后再决定点击或右键。
- 想读取某些浏览器、资源管理器、播放器、聊天软件界面中的可访问性信息。
不建议一开始就把 Acc 当成所有窗口控制的首选。普通 Edit、Button、ComboBox、SysListView32 这类标准控件,Control 系列更简单、更稳定。Acc 更像是标准控件路线失效后的补位工具。
二、准备工具:Acc.ahk 和 AccViewer
原帖最重要的两个工具入口是:
- Acc library (MSAA) and AccViewer download links:Acc.ahk 库和 AccViewer 下载入口。
- AccViewer Basic:更适合学习 Acc 函数调用方式的基础版查看器。
站内也已经有整理好的 Acc 资源,可以优先看 Acc窗口控制辅助增强库、Acc库获取窗口自动化高级用法示例、Acc获取鼠标下的文字。
使用方式通常是把 Acc.ahk 放进脚本同目录,或者放到 AHK 的 Lib 目录,然后在脚本里引用。
#Requires AutoHotkey v1.1
#Include <Acc>
F1::
try
{
oAcc := Acc_ObjectFromPoint(childId)
name := oAcc.accName(childId)
value := oAcc.accValue(childId)
role := oAcc.accRole(childId)
state := oAcc.accState(childId)
MsgBox, % "Name: " name
. "`nValue: " value
. "`nRole: " role
. "`nState: " state
. "`nChildID: " childId
}
catch e
{
MsgBox, % "Acc 读取失败:" e.Message
}
return
这个示例适合先验证 Acc 能不能读到鼠标下的元素。能读到名称和值,后面再考虑按路径稳定定位;读不到,说明目标软件可能没有暴露 MSAA 信息,或者需要换 UIA、图色、浏览器接口等方案。
三、AccViewer 应该怎么看
AccViewer 可以理解成 Acc 版的 Window Spy。你把十字准星拖到目标元素上,它会显示元素的名称、值、角色、状态、坐标、父子关系等信息。原帖里反复强调:AccViewer 能看到的信息,理论上就可以通过 Acc 函数读取。
最容易让新手卡住的是 Acc 路径。类似 1.2.3 的路径,意思是:从窗口或控件对应的起点对象开始,进入第 1 个子元素,再进入它的第 2 个子元素,再进入它的第 3 个子元素。也就是说,Acc 路径不是屏幕坐标,而是界面元素树里的“孩子编号路线”。
用路径读取对象的基本写法如下:
F2::
WinGet, hWnd, ID, A
vAccPath := "1.2.3" ; 这里换成 AccViewer 里看到的路径
try
{
oAcc := Acc_Get("Object", vAccPath, 0, "ahk_id " hWnd)
MsgBox, % "名称:" oAcc.accName(0) "`n值:" oAcc.accValue(0)
}
catch e
{
MsgBox, % "读取失败:" e.Message
}
return
有些 AccViewer 版本会显示逗号路径,例如 4,1,1,而 Acc_Get() 一般使用点号路径,例如 4.1.1。如果复制后不能用,先把逗号换成点号。
四、Acc_Get 常用命令怎么理解
Acc_Get() 是 AHK Acc 库里的封装函数,它帮你从窗口标题、句柄、路径和 ChildID 定位到可访问性对象,再读取对应属性或执行动作。常见命令可以这样理解:
| Cmd | 含义 | 常见用途 |
|---|---|---|
Object |
返回 Acc 对象本身 | 后续手动调用 accName、accValue、accLocation |
Name |
读取 accName |
按钮文字、菜单项名称、列表项名称 |
Value |
读取 accValue |
编辑框内容、地址栏内容、部分状态值 |
Action |
读取默认动作说明 | 查看元素能否点击、打开、切换 |
DoAction |
执行默认动作 | 类似点击按钮或触发菜单项 |
Keyboard |
读取快捷键提示 | 少数菜单或按钮会提供快捷键信息 |
如果只是读文本,可以先用 Name 或 Value。如果要做复杂判断,建议先取 Object,再自己调用属性和方法。
五、ChildID:为什么有时不是 0
Acc 里经常会遇到 ChildID。简单理解:0 通常表示对象本身,正整数可能表示对象内部的某个子项。比如某些 ListView、TreeView、菜单项或浏览器元素,焦点和选择项可能不是一个完整对象,而是通过 ChildID 表示。
所以同样是 accName(),有时要写 accName(0),有时要写 accName(childId)。用 Acc_ObjectFromPoint(childId) 时,函数会把鼠标下子项编号写到 childId 变量里,这就是为什么前面的示例要把它传给 accName。
六、焦点和选中项:accFocus / accSelection
原帖后面的问答里有一个很实用的点:accFocus 可以判断某些元素是否获得焦点,但返回值不一定直观。对于浏览器地址栏这类元素,可能返回 0 表示对象本身有焦点;对于列表控件,它可能返回当前焦点项的索引。
下面是原帖里提到的浏览器地址栏聚焦判断思路,路径会随着浏览器版本和界面布局变化,仅适合当作写法参考:
FirefoxUrlBarFocused(hWnd)
{
vAccPath := "application.tool_bar3.combo_box1.editable_text"
; vAccPath := "4.27.4.2"
oAcc := Acc_Get("Object", vAccPath, 0, "ahk_id " hWnd)
return oAcc.accFocus = "0" ? 1 : 0
}
ChromeUrlBarFocused(hWnd)
{
vAccPath := "pane.client.client2.client2.client.grouping.editable_text"
; vAccPath := "4.1.2.2.3.5.2"
oAcc := Acc_Get("Object", vAccPath, 0, "ahk_id " hWnd)
return oAcc.accFocus = "0" ? 1 : 0
}
如果你处理的是列表控件,accFocus 和 accSelection 的返回值可能是索引,也可能是一个可枚举对象。原帖给了一个读取焦点项和选中项的基础写法:
q:: ; 获取当前控件的焦点项 / 选中项
WinGet, hWnd, ID, A
ControlGetFocus, vCtlClassNN, % "ahk_id " hWnd
ControlGet, hCtl, Hwnd,, % vCtlClassNN, % "ahk_id " hWnd
oAcc := Acc_ObjectFromWindow(hCtl)
vFoc := oAcc.accFocus
vSel := oAcc.accSelection
if IsObject(vSel)
{
oSel := vSel, vSel := ""
while oSel.Next(vValue, vType)
vSel .= (A_Index=1 ? "" : ",") vValue
oSel := ""
}
MsgBox, % "焦点项:" vFoc "`r`n选中项:" vSel
oAcc := ""
return
这里也能看出一个实战原则:Acc 经常需要结合普通窗口命令一起用。先用 WinGet、ControlGetFocus 拿到窗口或控件句柄,再把句柄交给 Acc。
七、获取位置后再点击或右键
Acc 本身可以执行默认动作,例如 accDoDefaultAction 或 Acc_Get("DoAction", ...)。但它不等于完整鼠标模拟,尤其是右键菜单、拖拽、复杂点击时,往往还是要先用 Acc 取元素位置,再用普通鼠标命令或 ControlClick 处理。
Acc_Location(Acc, ChildId=0, ByRef Position="")
{
try Acc.accLocation(ComObj(0x4003, &x:=0)
, ComObj(0x4003, &y:=0)
, ComObj(0x4003, &w:=0)
, ComObj(0x4003, &h:=0)
, ChildId)
catch
return
x := NumGet(x, 0, "int")
y := NumGet(y, 0, "int")
w := NumGet(w, 0, "int")
h := NumGet(h, 0, "int")
Position := "x" x " y" y " w" w " h" h
return {x:x, y:y, w:w, h:h}
}
拿到坐标以后,可以点击元素中心:
F3::
oAcc := Acc_ObjectFromPoint(childId)
pos := Acc_Location(oAcc, childId)
if IsObject(pos)
{
Click, % pos.x + pos.w//2, % pos.y + pos.h//2
}
return
如果目标窗口支持后台点击,也可以把位置换算到窗口客户区后配合 ControlClick。这部分可以结合站内 控件自动化入门 和 窗口控制怎么选 一起看。
八、批量枚举:JEE_AccGetTextAll 的用途
原帖还提到一个很有用的诊断思路:当 AccViewer 给出的路径不准确,或者你不知道窗口里到底有哪些 Acc 元素时,可以用枚举函数把窗口里的文本和路径全部扫出来。jeeswg 的相关帖子是 Acc: get text from all window/control elements。
q:: ; 枚举活动窗口里的 Acc 文本和路径 WinGet, hWnd, ID, A Clipboard := JEE_AccGetTextAll(hWnd, "`r`n") MsgBox, % "已写入剪贴板" return
注意:JEE_AccGetTextAll 不是 Acc.ahk 原生函数,需要从对应帖子或整理版代码中引入。它更适合排查和定位路径,不建议在高频循环里一直跑,因为完整枚举复杂窗口可能会比较慢。
九、常用 IAccessible 方法速查
Acc.ahk 封装的是 MSAA 的 IAccessible 接口。下面这些方法名在脚本里经常出现:
| 方法 / 属性 | 用途 |
|---|---|
accName |
读取元素名称,常见于按钮文字、菜单项、列表项。 |
accValue |
读取元素值,常见于编辑框、地址栏、部分进度或状态元素。 |
accRole |
读取元素角色,例如按钮、文本、列表、组合框。 |
accState |
读取状态,例如聚焦、选中、不可用、隐藏等。 |
accLocation |
读取元素屏幕坐标和宽高。 |
accFocus |
读取当前焦点对象或焦点子项。 |
accSelection |
读取选中项,多个选中项时可能返回对象。 |
accDefaultAction |
读取默认动作说明。 |
accDoDefaultAction |
执行默认动作,常见效果类似点击或打开。 |
accParent |
获取父对象。 |
accChild |
获取子对象。 |
accChildCount |
获取子元素数量。 |
更完整的接口说明可以看微软文档里的 IAccessible interface。不过对于 AHK 实战来说,先掌握 accName、accValue、accState、accLocation、accDoDefaultAction 已经能解决不少问题。
十、常见坑和判断路线
1. AccViewer 能看到,不代表路径永远稳定。软件更新、布局改变、语言切换、窗口缩放,都可能让路径变化。重要脚本要尽量结合窗口类名、标题、控件句柄、元素名称一起判断。
2. Acc 路径有时要从控件句柄开始,而不是主窗口句柄。如果从主窗口开始路径很深,可以先用 ControlGet 拿到目标控件的 HWND,再对这个控件调用 Acc。
3. accValue 不一定能写入。有些元素能读值,但不允许设置值;有些元素看起来像编辑框,实际只暴露名称。写入文本时仍要考虑 ControlSetText、Send、剪贴板、COM、浏览器接口等方案。
4. accDoDefaultAction 不等于万能点击。它触发的是元素声明的默认动作。有些元素没有默认动作,有些默认动作和你想象的不一样。需要右键、拖动、复杂菜单时,通常还是取坐标后用鼠标命令。
5. Chrome 类浏览器可能需要开启可访问性。如果 Acc 读不到网页内容,可以尝试用 --force-renderer-accessibility 启动 Chrome,或在 chrome://accessibility/ 检查相关开关。但网页自动化长期来看更推荐浏览器控制、JS 注入或 HTTP 请求,站内可看 浏览器自动化怎么选。
6. Acc 和 UIA 不要混为一谈。Acc/MSAA 更老,很多传统软件和部分系统界面很好用;UIA 更新,面对现代界面更有优势。站内 利用ACC和UIA后台获取文本、UIA库获取窗口自动化高级用法示例 可以配合看。
十一、我的推荐学习顺序
如果你是第一次接触 Acc,我建议按这个顺序走:
- 先用 AHKInfo 和 Window Spy 判断有没有标准控件、类名、控件句柄。
- 能用
ControlGetText、ControlClick的,优先用普通控件命令。 - 普通控件命令拿不到时,用 AccViewer 拖十字准星看目标元素。
- 先用
Acc_ObjectFromPoint读鼠标下元素,确认 Acc 能读到信息。 - 再用
Acc_Get("Object", path, childId, "ahk_id " hWnd)固定路径。 - 需要批量查路径时,用
JEE_AccGetTextAll这类枚举工具辅助定位。 - 如果 Acc 路径非常不稳定,或者目标是现代复杂界面,再转 UIA、浏览器控制或图色路线。
这样学 Acc 会轻松很多:不要一开始就记 IAccessible 的全部方法,也不要急着写通用封装。先做到“能识别一个元素、能读一个值、能定位一个按钮”,再慢慢扩展到焦点、选中项、位置、默认动作。
站内延伸
- Acc窗口控制辅助增强库
- Acc库获取窗口自动化高级用法示例
- Acc后台取微信来新消息更新提醒
- Acc获取鼠标下的文字
- 利用ACC和UIA后台获取文本 By FeiYue
- UIA窗口控制辅助增强库
- AHK 自动化到底该用哪种方法:热键、控件、Acc、UIA、图片识别、浏览器控制
- 控件自动化入门:ControlGetText、ControlSetText、ControlClick
外部参考
- jeeswg's Acc tutorial (Microsoft Active Accessibility) (MSAA)
- Acc library (MSAA) and AccViewer download links
- AccViewer Basic
- Acc: get text from all window/control elements
- Microsoft IAccessible interface

评论(0)