来源说明:本文根据 AutoHotkey 论坛 Descolada 发布的 UIAutomation with a focus on Chrome 主题整理翻译,并结合本站已有的 UIA窗口控制辅助增强库、UIA库获取窗口自动化高级用法示例 做了取舍。原帖包含 UIAViewer 完整工具源码、示例和更新记录,本文不重复搬运大段源码,重点整理它对 UIA、Chrome/Edge 自动化和新手使用路径的启发。
如果你已经会用 ControlClick、Acc、图片识别或 Chrome.ahk,那么 UIA 可以理解为另一条路线:它不是按坐标点,也不是靠图片找,而是从 Windows 暴露出来的“自动化元素树”里找到按钮、编辑框、文档区域、菜单项,再调用对应的模式完成点击、取值、写入、滚动等操作。
一、这篇原帖主要讲什么
原作者当时的背景是:需要做浏览器,尤其是 Chrome 自动化,但又不能依赖外部自动化程序。于是他转向 Microsoft UI Automation 框架,并在 jethrow 的 UIA_Interface 基础上继续扩展,形成了现在比较常见的 Descolada 版 UIAutomation 库。
这个库的项目地址是:https://github.com/Descolada/UIAutomation。原帖还推荐配合 Wiki、Joe the Automator 的教学视频,以及 Microsoft UI Automation 说明来理解底层概念。
它的核心文件大致分为三类:
UIA_Interface.ahk:UIAutomation 框架的 AHK 封装,也是最核心的文件。UIA_Browser.ahk:面向 Chrome、Edge 等 Chromium 浏览器的辅助封装,例如获取页面元素、标签页、地址等。UIA_Constants.ahk:可选常量文件。好处是常量名更直观,注意它会创建较多全局变量,脚本中不要随意覆盖。
二、UIA 和 Acc 的关系
UIA 是 Microsoft 的辅助功能自动化框架,可以和用户界面中的可访问元素交互。它可以看作 Acc/MSAA 的后继方案,但并不是说 UIA 永远替代 Acc。实际写脚本时,Acc 和 UIA 经常要按目标软件来选:有的软件 Acc 更好取,有的软件 UIA 更完整,浏览器和现代应用里 UIA 往往更值得尝试。
本站已有 Acc库获取窗口自动化高级用法示例 和 UIA库获取窗口自动化高级用法示例,建议把这篇当成 UIA 路线的背景补充:先明白元素树、属性、条件、Pattern,再去看具体示例会轻松很多。
三、UIA 的基本模型:桌面是一棵树
UIAutomation 会把整个桌面组织成一棵由 AutomationElement 组成的树。根节点是桌面,桌面下面是各个窗口,窗口下面是工具栏、文档区域、按钮、菜单、编辑框等子元素。
使用 UIA 的第一步通常是先拿到某个窗口或控件对应的元素。常见入口包括:
ElementFromHandle():通过窗口句柄获取元素。ElementFromPoint():通过屏幕坐标获取鼠标下的元素。GetRootElement():获取桌面根元素,再向下查找。
拿到元素以后,再通过 FindFirst、FindAll、FindFirstByName、FindFirstByType 等方式继续找子元素。更底层的写法则是创建条件,例如按 Name、ControlType、AutomationId 等属性筛选。
四、最小示例:操作记事本
下面这个示例保留原帖思路,但把注释换成中文。它启动记事本,找到文档编辑区域,然后写入文本。注意:不同 Windows 版本、不同 UIA 版本下,记事本编辑区域可能表现为 Document 或 Edit,实际项目里要用 UIAViewer 或 Dump 工具确认。
#Requires AutoHotkey v1.1
#NoEnv
#SingleInstance Force
SetTitleMatchMode, 2
#Include <UIA_Interface>
Run, notepad.exe
UIA := UIA_Interface()
WinWaitActive, ahk_exe notepad.exe
npEl := UIA.ElementFromHandle(WinExist("ahk_exe notepad.exe"))
documentEl := npEl.FindFirstByType("Document")
documentEl.SetValue("Lorem ipsum")
ExitApp
这类写法比单纯 Send 稳定的地方在于:它不是把键盘输入发给当前焦点,而是直接找到 UIA 暴露出来的文档元素,再通过元素支持的模式写入值。但前提是目标控件确实暴露了可用的 UIA 信息。
五、先 Dump,再写自动化
UIA 新手最容易卡住的一点是:凭感觉猜元素名。正确做法是先把窗口树 Dump 出来,看看每个元素的 Name、ControlType、AutomationId、是否支持 ValuePattern 或 InvokePattern。
Run, notepad.exe
UIA := UIA_Interface()
WinWaitActive, ahk_exe notepad.exe
npEl := UIA.ElementFromHandle(WinExist("ahk_exe notepad.exe"))
MsgBox, % npEl.DumpAll()
documentEl := npEl.FindFirstByType("Document")
documentEl.SetValue("测试写入文本")
如果 Dump 出来的树里根本没有你想要的按钮或文本,就不要继续硬写查找条件。先换工具、换窗口层级、尝试激活可访问性,或者判断这个场景是不是更适合 Acc、Control、Chrome.ahk、JS 注入、图片识别。
六、计算器示例:按 Name 和 ControlType 找按钮
UIA 里最常见的定位方式是“名称 + 控件类型”。原帖用英文系统的计算器做例子,按钮 6 的名字是 Six,加号是 Plus,等号是 Equals。中文系统下名称可能不同,所以不要照抄英文 Name,要以自己机器上 Dump 出来的结果为准。
#NoEnv
#SingleInstance Force
#Include <UIA_Interface>
Run, calc.exe
UIA := UIA_Interface()
WinWaitActive, Calculator
cEl := UIA.ElementFromHandle(WinExist("Calculator"))
cEl.FindFirstBy("Name=Six").Click()
cEl.FindFirstBy("Name=Five AND ControlType=Button").Click()
cEl.FindFirstByName("Plus").Click()
cEl.FindFirstByNameAndType("Four", "Button").Click()
cEl.FindFirstByNameAndType("Equals", "Button").Click()
ExitApp
这个例子真正值得学的不是“怎么点计算器”,而是 UIA 的定位思想:能用稳定属性就不用坐标,能加控件类型就不要只靠名称,能通过工具确认就不要靠猜。
七、Chrome/Edge 的特殊点
这篇原帖标题里说“focus on Chrome”,重点就在于 Chromium 浏览器。普通窗口里 UIA 元素树可能直接可见,但 Chrome/Edge 的页面内容有时需要启用或唤醒可访问性,常见方式包括启动参数 --force-renderer-accessibility,或者给 Chrome_RenderWidgetHostHWND1 发送 WM_GETOBJECT 消息。
ChromiumActivateUIA(wTitle) {
WinGet, cList, ControlList, %wTitle%
if InStr(cList, "Chrome_RenderWidgetHostHWND1")
SendMessage, WM_GETOBJECT := 0x003D, 0, 1, Chrome_RenderWidgetHostHWND1, %wTitle%
}
这也是很多人用 UIA 抓浏览器页面时“有时能看到、有时看不到”的原因之一。不是 UIA 一定失效,而是 Chromium 内容区域的可访问性树可能还没准备好。
八、UIA_Browser 的用途
UIA_Browser.ahk 是对浏览器场景的封装。它不是 Chrome DevTools 协议,也不是 Selenium,而是继续走 UIA 路线,只是帮你减少一些找浏览器主窗体、当前文档、标签页、页面加载状态的样板代码。
#NoEnv
#SingleInstance Force
SetTitleMatchMode, 2
#Include <UIA_Interface>
#Include <UIA_Browser>
browserExe := "chrome.exe"
Run, %browserExe% -incognito --force-renderer-accessibility
WinWaitActive, ahk_exe %browserExe%
cUIA := new UIA_Browser("ahk_exe " browserExe)
cUIA.WaitPageLoad("New Tab", 3000)
Clipboard :=
Clipboard := cUIA.GetCurrentDocumentElement().DumpAll()
ClipWait, 1
if Clipboard
MsgBox, 页面信息已复制到剪贴板,可以粘贴到记事本查看。
else
MsgBox, 没有成功获取页面信息。
这个例子适合作为排查入口:先确认当前页面文档元素能不能 Dump 出来,再谈点击按钮、获取文本、操作表单。如果当前文档元素都取不到,后面的自动化逻辑大概率也不会稳定。
九、和 Chrome.ahk、JS 注入怎么选
UIA 操作浏览器的优点是:不一定需要开启调试端口,也不一定依赖外部驱动;它更接近“从用户界面可访问性树里操作页面”。缺点也很明显:元素暴露情况受浏览器、页面、语言、可访问性状态影响,复杂网页不一定比 DevTools 协议好控。
所以实际选型可以这样看:
- 网页结构明确、需要读 DOM、执行 JS、下载、网络响应:优先看 Chrome.ahk 或站内 浏览器自动化怎么选。
- 不能开调试端口、只能像用户一样操作界面:UIA_Browser 值得试。
- 目标是普通 Windows 软件窗口:先试 Control,再试 Acc/UIA,最后再考虑图片识别。
- 页面元素经常变化、UIA 树不稳定:不要硬撑 UIA,换 JS 注入或 DevTools 路线更稳。
十、使用 UIA 时最应该记住的几点
第一,UIA 不是万能找控件工具。它依赖目标程序是否正确暴露可访问性信息。软件不暴露、暴露不完整、不同语言 Name 不一样,都会影响脚本稳定性。
第二,写 UIA 脚本前先用工具看树。原帖提供的 UIAViewer 很有价值,本站也有 UIA/Acc 相关工具和示例。不要只靠肉眼看到按钮文字就开始写 FindFirstByName()。
第三,优先组合条件。只靠 Name 容易误匹配,能加 ControlType、AutomationId、父元素范围就尽量加。
第四,Chrome/Edge 页面要特别注意可访问性是否启用。必要时使用 --force-renderer-accessibility 或唤醒 Chromium 内容区域。
第五,UIA、Acc、Control、Chrome.ahk、图片识别不是谁替代谁,而是不同层级的工具。稳定脚本通常来自正确选型,而不是把一种方法用到所有地方。
站内延伸
总的来说,这篇原帖的价值不只是给了几个示例,更重要的是把 UIA 的思路讲清楚了:先把界面当成元素树,再按属性和模式去操作。只要能接受“先观察树,再写条件”的工作方式,UIA 会比坐标和盲发按键稳定很多。

评论(0)