关于 winapp-ui-automation
winapp-ui-automation 是供 Agent 和开发者通过命令行检查、操作运行中 Windows 应用界面的技能。它支持跨框架的 UI 自动化,并能协调并发的桌面工作流。
主要功能
- 01检查和搜索 UI 元素
- 02点击、调用并输入内容
- 03等待 UI 状态变化
- 04截取屏幕并录制操作
- 05协调并发 UI 工作流
适用场景
- 01开发期间验证 UI 状态
- 02自动化 Windows 应用测试流程
- 03通过可见界面行为排查问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ winappcli --skill winapp-ui-automation安装后在 Claude Code 中输入/winapp-ui-automation确认已加载。
附带文件references/ui-json-envelope.md
详细介绍
如果你无法亲自运行 winapp(没有 shell,或命令被拒绝),请向用户提供其项目所需的准确 winapp 命令,而不只是描述步骤。
适用场景
- 从命令行检查正在运行的 Windows 应用的 UI
- AI Agent 与 Windows 应用交互(点击按钮、读取文本、截取屏幕截图)
- 在开发或测试期间验证 UI 状态
- 不使用 Playwright 或 Selenium 自动化 UI 工作流
- 根据用户看到并可操作的内容,调试 WinUI 3、WPF、WinForms、Win32 或 Electron 应用的 UI
- 不适用于 WinUI 3 应用的 XAML 内部问题(值或绑定为何以特定方式解析、哪个 XAML 行创建了某个元素、实时编辑属性、UI 审查意见):请使用
winui-devtools
前置条件
对于 WinUI 3 XAML 源码/树、绑定或依赖属性的诊断与编辑,
请使用 winui-devtools 技能和 winapp devtools。这需要附加目标,或明确授权使用 --attach 进行注入;仅提供 PID 并不代表同意。AutomationId 可在这两种工具中用作选择器;生成的 slug 无法用于 DevTools。对于跨框架操作、屏幕截图、等待和无障碍功能,
请继续使用 winapp ui。
在驱动场景时测量 WinUI 工作,请使用 性能技能,在 UI 操作前后设置捕获标记。
- 对于 UIA 模式(任意应用):无需设置,可用于任何正在运行的 Windows 应用
- 对于会注入输入的动词(
click、hover、drag、touch、pen、scroll --wheel/--wheel-horizontal、send-keys --via send-input):需要未锁定的交互式桌面,且目标窗口能够置于前台。在锁定或安全桌面上,这些操作会立即失败,并返回no_interactive_desktop。基于 UIA 模式的动词(inspect、search、get-*、wait-for、set-value、invoke、scroll --direction/--to)适用于无头/锁定会话,在 CI 中应优先使用。 screenshot不属于该组:它始终需要独占执行时段,因此会排在其他 UI 工作流之后;捕获也可能需要可用的交互式桌面。引擎会在目标最小化时将其还原;如果无法进行帧捕获或使用了--capture-screen,则会尝试将目标置于前台。--capture-screen需要且仅需一个窗口。-w <hwnd>可指定一个窗口:截取该窗口的屏幕区域,包括其上方所有可见内容。如果-a匹配到多个顶层窗口或所属窗口,命令会在捕获前以invalid_arguments失败;请运行winapp ui list-windows -a <app>并使用-w <hwnd>重试。- 如果可能同时运行其他 UI 工作流,请为每个逻辑工作流设置一个 workflow id(见下文)。不设置也不会出错,但系统无法识别你的命令属于同一工作流。
与其他 UI 工作流协调
Windows 只有一个前台窗口、一个键盘焦点、一个光标和一个输入流。因此,winapp ui
会让桌面操作命令轮流协作,以免并发工作流抢走彼此的焦点或关闭彼此的菜单。此机制始终启用。只读命令永远不会等待。
跨命令保持桌面状态需要选择启用——不带 ID 时,每条命令都是一次性操作,结束后立即释放桌面:
# Set once per logical UI workflow — same value for cooperating calls, different values for
# independent workflows (even from the same agent).
$env:WINAPP_UI_WORKFLOW_ID = [guid]::NewGuid().ToString()
使用 Agent 操作时,以下规则尤为重要:
- 每次工具调用通常都会启动一个全新的 shell,且没有进程祖先关系回退机制,因此,只有注入的 ID 才能对命令分组。不带 ID 时,每次调用都是独立的工作流。
- 带 ID 的工作流会在最后一条命令执行后保留操作权四秒。这足以覆盖同一脚本中的连续命令;但在你思考期间,操作权会有意过期。仅在无法明确表示操作已完成时,将其视为后备机制,而不要依赖它来结束操作。
- **完成已知操作序列时,运行
winapp ui yield。**它会立即交出桌面,而不会让等待中的工作流白等用不到的四秒宽限期。重复交出操作权,或在宽限期过后才交出,都会安全成功。 - **思考间隔后,重新执行初始化操作。**其他工作流可能已经使用了桌面,因此请重新打开菜单/重新导航、重新解析元素,然后再操作。不要假定临时 UI 状态仍然存在。
- 对于已知操作序列,优先使用一个紧凑脚本,而不是多次往返调用:
winapp ui invoke View -w $hwnd; winapp ui search "Status bar" -w $hwnd; winapp ui click "Status bar" -w $hwnd; winapp ui yield。 record与其所属工作流共用操作权,因此运行期间,同一工作流中的点击和输入都会被记录——但前提是两个命令都带有相同的 ID。不带 ID 的record会在整个录制期间阻塞其他所有工作流。- **排队顺序遵循所有者优先,然后按 FIFO 排序。**活跃工作流可以继续发出命令,优先于其他已在等待的工作流;它交出操作权或宽限期过期后,再按到达顺序服务等待者。
- **等待时间不限,且可取消。**等待一秒后,stderr 会提示哪个操作占用了桌面以及已占用多久(使用
--json和--quiet时不显示);按 Ctrl+C 会以状态码130退出,并返回错误代码cancelled,命令不会执行。 - 没有硬性上限——长时间运行的脚本、无休止的录制或故障循环,都可能阻塞其他会修改界面的工作流,直到该操作完成或被停止。
永远不会等待的命令:status、list-windows、inspect、search、get-*、wait-for。
会等待操作权但不会独占桌面的命令(适用于无头/锁定会话):set-value、scroll-into-view、scroll --direction/--to、record。这些命令会修改应用,因此会排在其他工作流之后。在自己的工作流中,它们可以与其他共享操作重叠——record 正是通过这种方式捕获它所录制的 set-value 调用——但如果自己的工作流中有更早的 DesktopExclusive 命令,它们仍会等待。因此,click 后接 set-value 时,会按你写下的顺序执行。
会独占桌面的命令:invoke、click、drag、hover、scroll --wheel/--wheel-horizontal、
touch、pen、focus、send-keys、screenshot。
# Finish a workflow deliberately rather than leaving the desktop reserved for four more seconds.
winapp ui yield
常见模式
在特定容器中读取
winapp ui search "Welcome to MyApp" -a myapp --root MailRow --type Text --class-name TextBlock
winapp ui get-value Subject -a myapp --root MailRow --type TextBox
winapp ui wait-for Subject -a myapp --root MailRow --type Edit --value Ready --timeout 10000
winapp ui search --class-name CalendarSurface -a myapp # no text selector needed
在 search、get-property、get-value、wait-for 以及操作一个已选元素的命令(invoke、set-value、click、focus 等,但不包括 send-keys --target)中,可一起或分别使用 --root、--type 和 --class-name。
--type 和 --class-name 无需选择器即可使用;除 search 外,其他命令都必须匹配到且仅匹配一个元素。若匹配不明确,则会列出前五个匹配项及其 slug。单独使用 --root 不是查询。对于 set-value,请将 "" 作为选择器。
对于 click、hover、touch 和 pen,筛选条件不能与 --at 或 --path 同时使用。根元素必须唯一;只有其后代会被匹配。wait-for 会在每次轮询时重新解析根元素,即使它最初不存在也不例外。类型名称和完整的字面 ClassName 值均忽略大小写。
仅有以下类型别名:TextBox → Edit 和 TextBlock → Text。
有关完整的类型词汇、边界和错误行为,请参阅限定范围和类型的查询。
发现并交互
# See what's clickable, then screenshot for context
winapp ui inspect -a myapp --interactive; winapp ui screenshot -a myapp
# Click and verify the page changed
winapp ui invoke btn-settings-a1b2 -a myapp; winapp ui wait-for pn-settingspage-c3d4 -a myapp --timeout 3000; winapp ui screenshot -a myapp
# Fill a form and submit
winapp ui set-value txt-searchbox-e5f6 "hello" -a myapp; winapp ui invoke btn-submit-7a90 -a myapp; winapp ui screenshot -a myapp
查找可见文本并点击
# Search by text — output shows invokable ancestor
winapp ui search "Save changes" -a myapp
# Output:
# lbl-savechanges-a1b2 "Save changes" (120,40 80x20)
# ^ invoke via: btn-save-c3d4 "Save"
# Invoke by text — auto-walks to parent Button
winapp ui invoke 'Save changes' -a myapp
导航多页面应用
# Click nav item, wait for page, inspect what's available
winapp ui invoke itm-samples-3f2c -a myapp; winapp ui wait-for pn-samplespage-b4e7 -a myapp; winapp ui inspect -a myapp --interactive
在测试中选择精确操作
winapp ui invoke SettingsCategory -a myapp --action select
winapp ui invoke AgreeCheckbox -a myapp --action toggle-on --json
winapp ui invoke Open -w <dialog-HWND> --type Button --action invoke
使用 --action 可避免自动模式和祖先回退。省略该参数则使用现有的自动行为。使用 --root、--type 或 --class-name 时,筛选条件遵循与读取查询相同的匹配规则:在所选应用/窗口中必须恰好匹配一个元素,否则命令会失败且不执行操作,同时跳过祖先回退。有关支持的操作、范围、幂等切换和故障恢复,请参阅操作参考;有关操作结果,请参阅 JSON 信封。
消除重复元素的歧义
# Text matches by substring, but an element named exactly the text wins
winapp ui invoke Submit -a myapp # acts on Button "Submit", not Button "Submit Order"
# When several elements share that name, the error shows slugs for each — pick the right one
winapp ui invoke Save -a myapp
# → Selector matched 2 elements:
# [0] Button "Save" → btn-save-a1b2
# [1] Button "Save" → btn-save-c3d4
# Use the slug: winapp ui invoke btn-save-c3d4 -a myapp
关键概念
- 选择器方括号:
inspect和search的输出会用[方括号]显示选择器——在其他ui命令中使用括号内的值。选择器可以是 AutomationId(稳定,由开发者设置)或生成的 slug(例如btn-name-hash)。 - AutomationId 选择器:如果元素具有唯一的 AutomationId,它会直接成为选择器(例如
[MinimizeButton])。这类选择器在布局变化和本地化后仍然有效,是稳定定位的首选方式。 - Slug 选择器:如果不存在唯一的 AutomationId,则会使用生成的 slug(例如
[btn-close-a2b3])。格式为:prefix-name-hash。UI 发生变化后可能会失效。 - 纯文本搜索:
search和invoke接受纯文本——search Minimize会查找 Name 或 AutomationId 中包含 "Minimize" 的元素(子字符串匹配,不区分大小写)。无需特殊语法。对单个元素执行操作的命令会优先使用精确匹配的 AutomationId,其次匹配 Name 与文本完全一致(忽略大小写)的唯一元素;search仍会列出所有匹配项。 --interactive标志:筛选出可调用、点击或设置值的元素(包括文本文档),自动深度为 8——这是查看可操作内容的最快方式- 显示可调用祖先:如果搜索结果不可调用,则会显示最近的可调用父元素及其选择器
;链接:用;链接命令,即可在一次调用中执行多个操作,减少 Agent 往返次数-a与-w:使用-a按名称/标题/PID 查找应用。使用-w <HWND>稳定地定位窗口- 元素标记:切换项使用
[on]/[off],折叠状态使用[collapsed]/[expanded],可滚动容器使用[scroll:v]/[scroll:h]/[scroll:vh],以及[offscreen]、[disabled];可编辑元素使用value="..."
用法
连接并发现
# Core loop and examples: `winapp ui --help`. Connect and see interactive elements in one call:
winapp ui status -a myapp; winapp ui inspect -a myapp --interactive
检查元素树
winapp ui inspect -a myapp --interactive # elements you can invoke, click, or set-value; auto-depth 8
winapp ui inspect -a myapp --depth 5 # deeper tree at depth 5
winapp ui inspect txt-searchbox-e5f6 -a myapp # subtree rooted at element
winapp ui inspect btn-settings-a1b2 -a myapp --ancestors # walk up from element to root
winapp ui inspect -a myapp --hide-offscreen # hide offscreen elements
查找元素
winapp ui search Close -a myapp # finds elements with "Close" in name or automationId
winapp ui search Button -a myapp # finds elements with "Button" in name (also matches type names)
winapp ui search image -a myapp # case-insensitive substring match
截图
# App screenshot (may include owned windows in a labeled composite)
winapp ui screenshot -a myapp --output page.png
# Crop to element; capture with popups visible
winapp ui screenshot txt-searchbox-e5f6 -a myapp --output search.png
winapp ui list-windows -a myapp # use the main window's HWND below
winapp ui screenshot -w <hwnd> --capture-screen --output with-popups.png
# Bring window to foreground first (matches what the user is currently seeing)
winapp ui screenshot -a myapp --focus --output focused.png
即使明确指定了主 HWND,默认捕获也会包含所属窗口;它会生成一张带标签的合成图像,而不是多个单独的图像文件。对于打开的菜单、弹出面板、工具提示或教学提示中的项目,元素选择器会从该项目的弹出窗口裁剪,因此无需为其使用 --capture-screen。关于范围和屏幕上叠加层的位置,请参阅 截图。使用 --on sandbox 时,报告的截图路径是交付到主机上的目标路径。在附加了 WinUI DevTools 的应用中,截图不会包含其工具栏、标记、评论面板和检查器窗口。
录制视频(H.264 MP4)
将窗口或元素区域录制为 MP4。对于 Agent 和脚本,建议使用正数 --duration-sec N;否则录制会一直等待停止信号。npm 辅助工具要求提供 durationSec(整数,范围为 1–86400)。它们的中止信号会强制取消,而非正常停止。
# Record a window for 10s at 15 fps
winapp ui record -a myapp --duration-sec 10 --fps 15 --output demo.mp4
# Recommended agent evidence: MP4 plus timestamped JPEGs and an NDJSON index
winapp ui record -a myapp --frames --duration-sec 10 --fps 10 --output evidence.mp4 --json
# Include overlays/popups (captures from screen DC; may include occluding windows)
winapp ui record -a myapp --capture-screen --duration-sec 5 --output with-popups.mp4
# Show the mouse pointer and click rings (for demos of click-driven flows)
winapp ui record -a myapp --cursor --duration-sec 10 --output demo.mp4
# Programmatic stop: pipe a newline to stop and finalize the MP4 (for agent/script callers)
"" | winapp ui record -a myapp --json --output capture.mp4
- 默认情况下,
--duration-sec 0会一直录制,直到按下 Ctrl+C、输入换行符或重定向的 stdin 到达 EOF。 --frames会写入<output-name>.frames,其中包含清单、NDJSON 索引和变化的 JPEG 图像。它支持 1–30 fps 和--max-edge64–4096(默认值为 1280),上限为 1 GiB。使用elapsedMs来限定过渡时长。--cursor会绘制指针,并在每次点击时绘制一个圆环;添加--no-click-highlight可保留指针而不显示圆环。使用--frames时,每行frames.ndjson会在图像像素坐标中增加cursor(x、y、visible、inFrame)和clicks(button、kind、elapsedMs、x、y)。- 默认情况下,已有的录制输出会被拒绝。请使用新路径,或明确指定
--overwrite,在新录制完成后替换它们。之前的帧目录会被归档,而不会删除。如果发生部分失败,请保留报告的证据并遵循recoveryHint。 - 默认录制会在窗口原位绘制打开的菜单、弹出面板和工具提示;无需设置标志。它们会被裁剪到窗口边界内,其他进程的弹出窗口则需要使用
--capture-screen(请参阅 录制中的菜单和弹出面板)。 --capture-screen会从屏幕 DC 捕获,以包含叠加层和弹出窗口;捕获前会先将窗口置于前台。如果 WGC 不可用且未传入--capture-screen,CLI 会返回错误——请使用--capture-screen重新运行,以同意通过屏幕 DC 捕获。由于屏幕 DC 会捕获实际位于前方的内容,因此会在捕获前立即验证目标是否位于前台;如果激活被拒绝,命令会失败并返回foreground_not_target,且不会写入任何内容,而不是返回其他窗口的图像。- 如果提供的选择器与任何元素都不匹配,命令会立即失败并返回
element_not_found(而不是默默录制整个窗口)。 - 对于打开的菜单、弹出面板、工具提示或教学提示中的项目,选择器会从该项目的弹出窗口录制,而不是从其后方的窗口录制。
--json会将最终结果写入 stdout,并将每行一个 JSON 事件写入 stderr。
悬停(用于工具提示、弹出面板和悬停状态)
--dwell-time <ms> 设置悬停后等待的时长(默认值:800,范围:0–10000)。
winapp ui list-windows -a myapp # use the main window's HWND below, not the tooltip's
# Hover to trigger tooltip, then capture it (default 800ms dwell)
winapp ui hover btn-info-a1b2 -a myapp; winapp ui screenshot -w <hwnd> --capture-screen --output tooltip.png
# Longer dwell for apps with slow tooltip timers
winapp ui hover btn-info-a1b2 -a myapp --dwell-time 1200; winapp ui screenshot -w <hwnd> --capture-screen
点击或悬停屏幕坐标点
当需要按坐标点而非元素中心按下时(例如在 WebView2 页面内,或大型控件的某个部分),请传入 --at x,y,并使用 ui inspect 获取屏幕坐标,而不要使用选择器。--at 不能与选择器同时使用。
winapp ui click --at 1520,780 -a myapp # add --right or --double as needed
winapp ui hover --at 1520,780 -a myapp
发送键盘输入
合成按键——相当于键盘版的 click。用于方向键、Tab、Enter 导航、快捷键和逐键输入(与 set-value 的原子写入相对)。令牌以空格分隔:命名按键(enter、down、tab、esc、f5)、修饰键组合(ctrl+shift+t)、字面文本(hello)和原始虚拟键(vk=0xNN)。
# Keyboard navigation then commit
winapp ui send-keys "down down enter" -a myapp
# Type the literal words "down down enter" instead of pressing those keys (text= escapes each token)
winapp ui send-keys "text=down text=down text=enter" -a myapp
# Same intent, less typing: --verbatim types the whole argument literally (and keeps exact whitespace)
winapp ui send-keys "down down enter" -a myapp --verbatim
# Shortcut: select all and delete
winapp ui send-keys "ctrl+a delete" -a myapp
# Focus a field, then type text into it
winapp ui send-keys "Hello world" --target txt-name-a1b2 -a myapp
# Transport: --via post-message (default, HWND-targeted, bypasses UIPI) or send-input (OS-wide)
winapp ui send-keys "enter" -a myapp --via send-input
# Fire a global hotkey: win+... is refused by default (acts on the shell); opt in with --allow-system-keys
winapp ui send-keys "win+shift+v" -a myapp --via send-input --allow-system-keys
# Screen-reader command: hold capslock or insert (Narrator+Ctrl+F12 toggles Narrator developer mode)
winapp ui send-keys "ctrl+capslock+f12" -a myapp --via send-input
- 屏幕阅读器命令将
capslock或insert作为修饰键按住(ctrl+capslock+f12),并且必须使用--via send-input;因为屏幕阅读器不会接收已发布的按键,所以post-message会报错。如果没有运行屏幕阅读器,按住capslock会开启或关闭 Caps Lock(命令会发出警告);按住insert可避免这种情况。 - 以修饰键开头但中间部分未知的令牌(
ctrl+a+b)会报错,而不会被视为字面文本;请使用text=或--verbatim输入。 - 默认的
post-message以 HWND 为目标,可跨完整性级别工作,但无法触发WH_KEYBOARD_LL全局快捷键。它会自动将目标重定向到目标窗口的焦点子控件,因此经典 Win32/WinForms 子窗口控件(例如编辑框)可以接收输入。WinUI 3 / UWP / XAML 控件没有窗口句柄,会忽略已发布的WM_CHAR/WM_KEYDOWN——post-message无法向它们传递按键或文本(命令会发出警告,但仍以退出代码 0 结束,因为PostMessage无法确认是否传递成功)。对于 WinUI 3 / UWP / XAML 应用,请使用--via send-input。 - 与按键/修饰键名称冲突的令牌(例如
enter、down、ctrl+a)会被作为相应按键按下。要将其作为字面文本输入,请在前面加上text=——text=enter会输入单词 "enter";串联多个text=令牌即可输入字面短语,例如text=down text=down text=enter。在text=的值中使用反斜杠转义,可输入令牌解析器原本会合并的空白字符:\s→空格,\t→制表符,\n→换行符,\r→CR,\\→反斜杠(例如,text=a\s\sb→ "a b")。如果整个参数都是字面文本,请使用--verbatim,而非逐个转义令牌:它会原样输入整个 keys 参数(不解析按键/组合键/vk=/text=),并保留精确空白——send-keys "down down enter" --verbatim会输入这些单词。(--verbatim不会解码反斜杠转义;要输入控制字符,请使用text=令牌。) send-input会发送完全真实的输入,但输入会发往前台窗口;从提升权限的进程向 AppContainer/AppX 注入时会被 UIPI 阻止。它会拒绝系统保留组合键(win+l、alt+f4、ctrl+shift+esc、ctrl+alt+del、alt+tab、……),因为这些组合键作用于操作系统/外壳,而非仅作用于目标——传入--allow-system-keys即表示允许(例如,触发 PowerToys 的win+shift+v或win+r等全局快捷键),或者使用--via post-message(限定于窗口)将其直接发送到窗口。即使使用--allow-system-keys,win+l和ctrl+alt+del仍会被阻止——win+l会通过LockWorkStation()锁定工作站(自动化无法解除),而ctrl+alt+del是安全注意序列(SAS),无论是否启用该标志,Windows 都会丢弃注入的此类输入,因此会报错(invalid_arguments),而不会错误地报告成功。在已锁定/安全桌面上,send-input会立即失败并返回no_interactive_desktop。- 逐键事件:两种传输方式都会让命名按键/组合键触发真实的
KeyDown。对于输入的字面文本,--via send-input会将每个字符映射到其 VK(+Shift),让每个字符触发真实的KeyDown和由操作系统合成的WM_CHAR(TextChanged)——如果下游逻辑依赖KeyDown(例如 WinUI 3/WPFTextBox),请使用此方式;使用前先将目标窗口置于前台。--via post-message会向焦点子控件逐字符发布WM_CHAR(触发TextChanged,并将正确的文本写入可跨完整性级别接收输入的经典 Win32 控件),但不会逐字符触发KeyDown;而且由于 WinUI 3 / UWP / XAML 控件没有窗口句柄,完全无法向它们传递输入(命名按键和文本都不行);此类控件请使用--via send-input。 - 使用
--via send-input输入较长字面文本时,会自动限速:文本会被拆分成较小的字符块,每次通过一次SendInput注入一块,并在同一轮注入的各块之间短暂停顿,让目标有时间处理输入队列并确保每个字符都能输入。一次不间断的突发输入会使队列溢出并静默丢弃字符,即使命令报告成功也是如此。限速仅适用于单次较长的文本输入——短文本、按键名称和修饰键组合(包括ctrl+a delete这样的序列)不会增加延迟;如果负载大到需要限速,命令会发出信息性警告。由于每个限速字符块都会输入到当前前台窗口,send-input会在每个后续字符块注入前重新确认目标仍处于前台;如果注入过程中焦点离开目标,则会以foreground_not_target中止,避免焦点中途变化后剩余文本被输入到其他窗口(会改变焦点的组合键,例如alt+tab,不受此规则限制,也不会被视为焦点偏移)。对于大批量文本,若控件支持,请优先使用set-value(原子操作,无需按键或窗口处于前台)。
拖动(重新排序、调整大小、滑块、拖放)
使用 drag <from> <to>,在一个位置按下鼠标按钮,移动到另一位置,然后松开;每个端点都是元素选择器(使用元素中心)或来自 ui inspect 的屏幕 x,y 坐标。该命令使用 SendInput 和中间移动,让应用收到逼真的 WM_MOUSEMOVE 事件流。
# Reorder one item onto another (center → center)
winapp ui drag itm-card-9f8e itm-slot-2c1a -a myapp
# Element center → screen coordinates (as reported by `ui inspect`)
winapp ui drag itm-card-9f8e 300,400 -a myapp
# Raw screen coordinates → screen coordinates
winapp ui drag 120,200 480,200 -a myapp
# Right-button drag
winapp ui drag itm-card-9f8e itm-trash-0001 -a myapp --right
- 选择器会从/向元素中心拖动;
x,y是屏幕坐标,使用与ui inspect/search报告的相同坐标系。拖动前会重新解析元素端点;如果元素仍在动画中,则会以target_moved失败;在已锁定/安全桌面上,拖动会以no_interactive_desktop失败。
触控手势(轻点、滑动、捏合、拉伸、长按)
注入合成触控。触点锚点可以是元素选择器(元素中心),也可以是明确的 --at x,y 屏幕坐标。优先使用现代合成指针设备,不可用时回退到旧版触控注入 API。
# Tap an element center; or tap explicit screen coordinates
winapp ui touch btn-ok-1a2b -a myapp
winapp ui touch -a myapp --at 320,240
# Long-press, swipe, and two-finger pinch/stretch (zoom)
winapp ui touch tile-photo-7b3c -a myapp --gesture long-press --hold-ms 600
winapp ui touch -a myapp --at 100,300 --gesture swipe --to-point 400,300
winapp ui touch img-map-9f8e -a myapp --gesture pinch --distance 200
winapp ui touch img-map-9f8e -a myapp --gesture stretch --distance 200 --angle 90
- 手势:
tap(默认)、double-tap、long-press、swipe、pinch、stretch。--fingers可设为 1–10(捏合/拉伸始终为 2)。--angle可设为 0–359,用于设置捏合/拉伸时手指的轴向(0 为水平,90 为垂直)。如果没有非零且位于前台的目标,则拒绝执行(no_target/foreground_not_target/no_interactive_desktop);每个坐标都会根据目标窗口进行检查,点位在窗口外时会发出非致命警告(在--json中为warnings[]),但仍会继续注入,与鼠标命令一致。如果设备不支持注入触控,命令会返回真实的 Win32 错误,而不会虚报成功。在远程桌面/VM 会话中,即使退出码为 0,也不保证事件已送达;命令会附加送达情况不确定的警告(在--json中为warnings[]);请通过截图验证效果。
笔/手写笔(轻点和墨迹笔划)
注入合成笔输入(Windows 10 1809+)。目标可以是元素中心、明确的 --at,也可以是完整的 --path 墨迹笔划,并可控制压力/倾斜/橡皮擦。
# Pen tap at element center; firm tap at explicit coords
winapp ui pen canvas-1a2b -a myapp
winapp ui pen -a myapp --at 320,240 --pressure 0.8
# Draw an ink stroke, or erase along one
winapp ui pen -a myapp --path "100,100 150,120 210,140 260,120"
winapp ui pen -a myapp --path "100,100 260,100" --eraser
--pressure范围为 0.0–1.0,--tilt-x/--tilt-y范围为 ±90°,--eraser用于橡皮擦端。注入安全要求与touch相同:必须有非零且位于前台的目标窗口,并检查每个墨迹点(在窗口外 → 非致命的warnings[]提示,但仍会继续注入)。远程桌面上的笔输入路由尤其不可靠——退出码为 0 可能表示调用成功,但没有笔输入送达应用;命令会附加送达情况不确定的警告(在--json中为warnings[])。请在本地交互式桌面上验证笔输入工作流。
读取元素状态
# Read text/value content (works for RichEditBox, TextBox, ComboBox, Slider, labels)
winapp ui get-value doc-texteditor-53ad -a notepad
winapp ui get-value SearchBox -a myapp
winapp ui get-value CmbTheme -a myapp # reads ComboBox selected item via SelectionPattern
# Check toggle/selection state, value, scroll position
winapp ui get-property chk-agreecheckbox-b2c3 -a myapp --property ToggleState
winapp ui get-property txt-textbox-a4b1 -a myapp --property Value
winapp ui get-property cmb-modellist-d5e6 -a myapp --property IsSelected
# Read formatting across the whole text document (not the selection)
winapp ui get-property Document -a myapp --property FontWeight --json
# See what has keyboard focus
winapp ui get-focused -a myapp
对于 get-focused,-a 会包含所选进程中的窗口;-w 会将焦点限制在该确切窗口,不包括其所属弹出窗口。即使控件未提供自己的进程 ID,也可能通过父窗口属于目标。有关焦点无匹配和查询错误的处理方式,请参阅 references/ui-json-envelope.md。
要检查空白或已清空的字段,请使用 winapp ui wait-for SearchBox -a myapp --value ""。
JSON 输出以及如何单独读取辅助功能标签,请参阅空值行为。
get-property 还接受 FontName、FontSize、ForegroundColor、IsItalic 和
StrikethroughStyle。省略 --property 可包含全部六种格式属性。
应将 Mixed、NotSupported 和 Unavailable 视为不同的状态,而不是格式值。名称区分大小写;未知名称会以 invalid_arguments 失败。
有关单位和状态含义,请参阅格式参考;有关 JSON 结构,请参阅 references/ui-json-envelope.md。
设置值
为通过键盘输入准备控件,请使用 winapp ui focus <selector> -a <app>
(在 Sandbox 中操作时保留 --on sandbox)。此命令会激活控件所在的窗口,并在成功前验证窗口同时处于前台且已获得键盘焦点。它会先还原最小化的窗口。如果命令提示目标窗口已最小化(某些应用,例如 Calculator,会在还原前隐藏界面),请运行
winapp ui focus -a <app>(不带选择器)以还原并激活窗口,然后重试。不要通过截图来解决焦点问题。如果激活被拒绝,请检查是否存在阻止操作的对话框,并请用户激活预期窗口;不要循环重试,也不要切换到本地桌面。有关失败代码和恢复方法,请参阅焦点参考。
set-value 通过回退链以编程方式写入(不发送按键,也不需要窗口处于前台):ValuePattern → RangeValuePattern(数值)→ 对仅支持 TextPattern 的编辑控件使用 LegacyIAccessible put_accValue。
winapp ui set-value txt-searchbox-e5f6 "hello" -a myapp # TextBox/ComboBox via ValuePattern
winapp ui set-value sld-volume-b2c3 75 -a myapp # Slider via RangeValuePattern
winapp ui set-value doc-compose-9f3a "hello" -a myapp # RichEdit/compose box via LegacyIAccessible
- 只要无障碍功能实现了
put_accValue,LegacyIAccessible 回退方式就能操作不公开 ValuePattern 的富文本编辑/撰写控件(原生 Win32 富文本编辑控件以及 Chromium/Electron/WebView2 撰写框通常都支持)。 - WinUI 3
RichEditBox和 WPFRichTextBox不支持以编程方式设置值——这是有意设计的:对于 UI Automation 的值 API,它们是只读的(支持 Text 模式,但没有可设置的 Value 模式)。set-value会报出明确错误;若要在这些控件中输入,请改用send-keys(需要桌面已解锁且窗口处于前台)。注意,get-value仍可通过 TextPattern 读取它们。
滚动容器
# Find scrollable containers — look for [scroll:v] (vertical) or [scroll:h] (horizontal)
winapp ui search scroll -a myapp
# Output:
# pn-scrollview-bfef Pane "scrollView" [scroll:v] (2127,296 1191x965)
# pn-scrollviewer-bfb1 Pane "scrollViewer" [scroll:h] (2127,296 1191x216)
# Scroll vertically
winapp ui scroll pn-scrollview-bfef --direction down -a myapp
# Scroll to top/bottom
winapp ui scroll pn-scrollview-bfef --to bottom -a myapp
# Scroll and then inspect for newly visible elements
winapp ui scroll pn-scrollview-bfef --direction down -a myapp; winapp ui search TargetItem -a myapp
# Synthesize real mouse-wheel input over the element (1 = one notch up, -1 = down) — tests wheel handlers (zoom, custom scroll)
winapp ui scroll img-map-a1b2 --wheel -1 -a myapp
# Horizontal (tilt) wheel: 1 = one notch right, -1 = left — pans timelines and other horizontal-wheel handlers
winapp ui scroll pn-timeline-c3d4 --wheel-horizontal 3 -a myapp
等待 UI 状态
winapp ui wait-for btn-submit-a1b2 -a myapp --timeout 5000
winapp ui wait-for itm-status-c3d4 -a myapp --value "Complete" --timeout 5000
提示
- 将
--interactive与inspect一起用作首条命令——它只显示可操作的内容 - 使用
;串联命令以减少往返次数(关于为何不使用&&,请见下文说明) - 使用输出中的 slug 定位特定元素——它们经过哈希验证,且对 shell 安全
- 使用纯文本搜索查找元素:
search Minimize、invoke Submit - 当文本匹配到多个元素时,名称与该文本完全一致的元素是唯一胜出者;若仍有多个匹配项,错误信息会显示每个元素的 slug——请选择正确的一个
- 在 invoke 后使用
get-property --property ToggleState验证复选框/切换控件的状态 scroll会自动查找最近的可滚动父级- 要捕获打开的菜单、飞出菜单、工具提示或教学提示中的某个项目,请将其选择器传给
screenshot或record;捕获内容来自其弹出窗口 - 当需要在窗口上保留弹出内容,或捕获应用不拥有的叠加层时,请按照 Screenshot 的说明使用
-w <hwnd> --capture-screen选择窗口 - 按照 Hover 的说明,在原位捕获工具提示和由悬停触发的 UI
- 使用
--focus在捕获前将目标窗口置于前台,而不切换到屏幕 DC 捕获(默认捕获路径使用 Windows.Graphics.Capture,即使窗口被遮挡也能工作) - 使用
--hide-disabled和--hide-offscreen减少干扰信息
为什么用 ; 而不是 &&
使用 ;(而不是 &&)串联命令。PowerShell 的 && 运算符在原生命令行工具向 stderr 写入内容或使用 ANSI 转义序列时可能会卡住——这会导致管道死锁。; 会无条件运行每条命令,避免此问题。这也更适合 Agent 工作流:即使 invoke 以非零退出码结束,通常也仍需要运行截图,以查看出了什么问题。
文件对话框的解决方法
文件打开/保存对话框是支持 UIA 的标准 Windows 对话框。请使用现有命令与其交互:
# 1. Trigger the dialog (e.g., click "Open File" button)
winapp ui invoke btn-openfilebtn-a2b3 -a myapp
# 2. Find the dialog window
winapp ui list-windows -a myapp
# → Shows the main window + the dialog HWND
# Note: untitled zero-size windows are hidden by default; use --show-hidden to include them
# 3. Target the dialog, type the file path, and confirm
winapp ui set-value txt-1148-c4d5 "C:\path\to\file.png" -w <dialog-hwnd>
winapp ui invoke btn-open-e6f7 -w <dialog-hwnd>
注意:标准文件对话框中的文件名输入框通常具有 AutomationId 1148。使用 inspect -w <dialog-hwnd> --interactive 查找实际的 slug。
JSON 输出封装
ui inspect、ui get-focused、ui search 和 ui wait-for 的 --json 封装在 v0.3.1 中进行了调整。DPI 上下文和带类型的 get-property 元素在 v0.6.3 及更高版本中可用。主要变化:
ui inspect --json现在将元素嵌套在windows[].elements[]下(之前是扁平的elements[])。- 每个被检查的窗口和
ui status --json目标都会报告windowDpi、scale、dpiAwareness和coordinateSpace: "physical-screen-pixels"。这些是目标窗口的 DPI 上下文。如果无法读取 DPI,所选目标会立即失败,而不会默认使用 96;如果次要窗口在遍历期间消失,则会附带dpiError,并省略这四个上下文字段。 - 成功的
ui get-focused --json查询会输出{ "hasFocus": false }或{ "hasFocus": true, "element": {...} }(之前是裸null);查询失败时会以非零状态退出,并在 stderr 上输出标准 JSON 错误,而不是返回hasFocus结果。 ui search --json返回{ "matchCount", "hasMore", "matches" };ui wait-for --json返回{ "found", "waitedMs", "element"?, "timedOut" }。ui get-property --json会保留elementId及其字符串值properties映射,并添加经过清理、带类型的element。- 带类型的元素使用
type(而非controlType),并以物理屏幕像素表示数值型x、y、width和height。0,0,0,0是 UIA 的空矩形/无显示 UI 矩形;isOffscreen仍是独立字段。 - 搜索和 wait-for 元素可能包含
invokableAncestor字段(形状与元素相同)。 - 已移除每个元素的
id、parentSelector和windowHandle— 请使用selector作为公开句柄。
包含示例的完整架构:references/ui-json-envelope.md。
在 Windows Sandbox 中自动化
winapp run . --on sandbox --detach
winapp ui inspect --on sandbox -a MyApp
winapp ui screenshot --on sandbox -a MyApp -o .\result.png
在后续 UI 命令之前使用 --detach,并在访客 PID 或窗口句柄中保留 --on sandbox。将相同的 WINAPP_UI_WORKFLOW_ID 注入协同运行的访客调用;这些调用全部完成后,使用 winapp ui yield --on sandbox 结束。
使用 winapp-sandbox 处理设置授权、已连接客户端要求、设置/重新连接期间的短暂焦点变化,以及捕获恢复。应用截图和录制内容(包括默认文件名和 --frames 目录)都会返回到主机。
相关技能
winapp-sandbox:在隔离的 Windows Sandbox 中运行和自动化应用winapp-setup:将 Windows SDK 添加到项目winapp-package:将应用打包为 MSIX
故障排查
| 错误 | 原因 | 解决方案 |
|---|---|---|
| "No running app found" | 名称错误或应用未运行 | 尝试使用进程名称、窗口标题或 PID |
| "Multiple windows match" | 多个窗口与 -a 匹配 | 使用列出的选项中的 -w <HWND> |
| "Selector matched N elements" | 文本查询匹配了多个元素 | 使用错误中显示的建议中的 slug,或使用 inspect 输出中的 slug |
| "Element may have changed" | slug 哈希与当前元素不匹配 | 重新运行 inspect 以获取最新的 slug |
| "does not support any invoke pattern" | 无法调用该元素 | 如果存在可调用的祖先 slug,错误中会显示它——请使用该 slug |
| "No UIA window found" | UIA 无法看到该窗口 | 使用 list-windows 查找 HWND,然后使用 -w |
| 截图中未显示弹出窗口 | 默认捕获路径不包含无所有者的覆盖层 | 如需选择打开的菜单或弹出面板中的某一项,请传入其选择器。如需原位显示弹出窗口或捕获无所有者的覆盖层,请按照 截图 中的说明,使用 -w <hwnd> --capture-screen 选择窗口 |
--capture-screen 返回 foreground_not_target | Windows 拒绝了激活请求(焦点窃取防护、UAC 提示或其他窗口自行激活),因此屏幕捕获会记录错误的窗口 | 单击目标窗口,关闭抢占焦点的窗口,然后重试;或者移除 --capture-screen,直接捕获窗口 |
录制期间出现 element_not_found | 已提供选择器,但元素不在树中 | 重新运行 inspect 或 search 以获取新的选择器 |
录制期间出现 ambiguous_selector | 纯文本选择器匹配了多个元素 | 使用错误消息建议中的 slug,或使用 inspect 输出中的 slug |
| 录制期间 WGC 不可用 | WGC 捕获初始化失败;不会静默回退 | 检查 GPU/驱动程序;使用 --capture-screen 明确请求屏幕 DC 捕获 |
CLI 参考
运行 winapp <command> --help 查看当前命令选项,或运行 winapp --cli-schema 查看完整的机器可读命令架构。