从命令行检查并与之交互运行Windows应用程序。 由 AI 代理和开发人员用于 UI 测试、调试和自动化。
概述
winapp ui提供用于检查和与Windows应用 UI 交互的命令。
使用Windows UI 自动化(UIA)。 适用于任何Windows应用 - WPF、WinForms、Win32、Electron 和 WinUI 3。
大多数命令通过 UIA 模式(无输入注入)驱动应用。 异常会注入实际输入:ui click/ui hover/ui drag使用鼠标模拟、ui touch/ui pen合成触摸和笔/触笔输入,并ui send-keys合成键盘输入,用于 UIA 模式无法驱动的控件和方案。
Important
交互式桌面要求(输入注入谓词)。click、、hoverdrag、、touch、pen、scroll --wheel和send-keys --via send-input合成 OS 级输入,因此他们需要具有前台目标窗口的解锁交互式桌面。 在 锁定的工作站或安全桌面 (LogonUI/UAC)上,它们无法通过 no_interactive_desktop (与提升/foreground_not_target 案例不同)快速注入和失败。
touch
/
pen 此外,当没有窗口解析no_target时();目标窗口外的坐标是 非致命警告 ( warnings[] 文本模式下的条目 --json或警告行)并继续注入 - 与鼠标谓词一致。 其他一切(inspect、、search、get-propertyget-value、wait-for、set-value、、、) invokescroll --direction/--to 通过 UIA 模式驱动应用,并且对无外设/锁定会话友好。
screenshot 是非注入谓词中的例外:它需要独占轮次,其捕获可能需要一个可用的交互式桌面,因为引擎还原最小化的目标,并在帧捕获不可用或使用 --capture-screen 时回退到前台。 首选 CI 中的 UIA 模式谓词;为真正需要实际输入的方案保留注入谓词。 在注入之前,手势谓词还 重新解析目标元素,如果目标元素 仍在动画/重新定位,而不是在空白处登陆输入,则拒绝 target_moved 该元素。
快速入门
# Connect to any app and see its UI tree
winapp ui inspect -a notepad
# Find specific elements
winapp ui search Button -a notepad
# Activate an element
winapp ui invoke Close -a notepad
# Take a screenshot
winapp ui screenshot -a notepad
在Windows沙盒中运行 UI 自动化
若要使自动化远离桌面,请添加到 --on sandbox 运行命令和 UI 命令:
winapp run . --on sandbox --detach
winapp ui inspect --on sandbox -a MyApp
winapp ui invoke --on sandbox SubmitButton -a MyApp
--detach 在启动后返回;如果没有它, run 请等待应用退出。 保留 --on sandbox 每个来宾命令,包括使用 PID 或窗口句柄的来宾命令。
有关客户端要求、简要设置/重新连接焦点更改、工作流协调和主机输出传递,请参阅Windows沙盒执行。
作用域查询和类型化查询
winapp ui search "Welcome to MyApp" -a myapp --root MailRow --type Text --class-name TextBlock
winapp ui get-value Subject -w 123456 --root MailRow --type TextBox
winapp ui get-property Subject -a myapp --root MailRow --type Edit --property Value
winapp ui wait-for Subject -a myapp --root MailRow --type Edit --value "Ready" --timeout 10000
search、 get-property、 get-value和 wait-for 接受这些可选筛选器。
选择器和每个提供的筛选器必须与 相同的元素匹配:
-
--root <selector>只搜索一个唯一匹配的根的后代,从不搜索根本身。 使用 AutomationId 或 sluginspect消除歧义。 匹配多个元素的根会失败ambiguous_selector,即使一个匹配项是可调用的。 缺少的根不会生成匹配项。 找到根目录后,即使没有后代匹配,查询也不会搜索不相关的弹出窗口。 查询不受显示深度的限制inspect。 -
--type <control-type>匹配 UIA 控件类型,忽略大小写。 唯一的别名是TextBox→Edit和TextBlock→Text。 未知名称(包括数字 ID 和通配符表达式)失败。invalid_arguments -
--class-name <literal>匹配提供程序的整个 UIAClassName,忽略大小写。 它不是子字符串、通配符或正则表达式。 用于get-property --property ClassName发现提供程序的值;类名不需要等于 UIA 控件类型。
筛选的查询使用 UIA 的 控件视图,该视图显示相同的 inspect视图。
仅原始视图中公开的提供程序节点不会返回;用于 inspect 查找包含控件及其选择器。
支持所有 41 种官方类型:Button、、CalendarCheckBox、、、 ComboBoxSpinnerDataItemDataGridThumbGroupCustomSplitButtonMenuBarMenuListListItemMenuItemImageEditTreeToolTipTreeItemDocumentTextSeparatorTabItemScrollBarRadioButtonSliderProgressBarStatusBarHyperlinkTabToolBarTableHeaderItemTitleBarPaneHeaderWindow、 。 SemanticZoomAppBar
wait-for
在每个轮询中再次解析根选择器,因此该根可能会在命令启动后显示。 因此 --gone,不存在的根意味着没有匹配的后代;不明确的根是错误,而不是成功。
中断的查找不是消失的证据:如果在读取期间 --value 删除元素或替换元素,则下次轮询会再次检查;其他查找或读取错误将失败命令。
-w <HWND> 将根发现限制为该窗口的 UIA 树。 此外 -a,根发现还可以找到应用的弹出窗口。 完全的根 AutomationId 匹配优先于所有这些窗口中的子字符串匹配项;多个完全匹配项仍然失败,ambiguous_selector
即使另一个窗口具有相同的 AutomationId,根 slug 也会选择该元素。 如果替换了所选根,则其旧的 slug 不再匹配;如果要轮询跟踪替换,请使用 AutomationId 或名称根。
当存在筛选器时,读取单个元素的命令会失败,如果多个元素保持不变,则命令会失败 ambiguous_selector ;缩小筛选器范围或使用唯一的污点。 精确 AutomationId 匹配项在筛选范围内保留优先于子字符串匹配项。 省略所有三个选项都会保留现有查询行为。
协调并发 UI 工作流
Windows只有一个前台窗口、一个键盘焦点、一个光标和一个输入流。 当两 winapp ui 个工作流同时在同一个登录桌面上运行时,他们可以从对方窃取焦点、关闭刚刚打开的菜单或从挂起的单击下移出目标。
仲裁始终处于开启。 触摸物理桌面的每个 winapp ui 命令都会轮流,无需设置,也无法将其关闭,因此两个代理永远无法键入彼此的窗口。 只读命令保持并发运行。
命令之间的连续性是选择加入。 默认情况下,每个命令都是自包含的一次性命令:它会等待轮次、执行其工作并立即释放桌面。 若要跨多个命令保留桌面,请向他们提供相同的工作流 ID:
# Set once per logical UI workflow
$env:WINAPP_UI_WORKFLOW_ID = [guid]::NewGuid().ToString()
温馨提示:
- 工作流 ID 命名一个逻辑工作流 , 不一定是整个代理, 也不一定是一个应用。 对协作命令 使用相同的值( 录制加上应捕获的单击):对独立工作流 使用不同的 值,即使一个代理同时启动也是如此。
- 没有 ID,每个命令都是独立的一次性命令。 它仍然仲裁,但它没有优雅,并把桌面从它完成的那一刻。 两个 no-id 命令是单独的工作流,即使从同一 shell 启动也是如此。
- Fresh-shell 和自适应主机必须注入相同的值。 如果每个命令在新的 shell 中运行(即大多数代理工具调用的工作原理),则唯一可以分组的命令是显式
WINAPP_UI_WORKFLOW_ID传递到每个合作调用中。 - 四秒宽限可保护紧密突发,而不是模型推理。 只要下一个命令在四秒内开始,具有 ID 的工作流就会保持其轮次。 这涵盖一个脚本中的背靠背命令;当模型正在思考时,它有意过期。 这是一个回退,当你不能说你完成 - 当你可以,运行
winapp ui yield而不是等待它。 - 自适应工作流必须重新获取、重新验证和重播。 在推理间隙之后,另一个工作流可能已使用桌面,因此请重新打开菜单,重新解析元素,然后采取行动。将已知的端到端序列作为一个紧密脚本发送,而不是在认为时按住桌面。
- 排序首先是所有者相关性,然后是 FIFO 等。 当工作流处于活动状态或处于其正常状态时,即使其他工作流已经在等待,它也可能继续发出命令。 运行
winapp ui yield或其宽限过期后,等待工作流将按严格的到达顺序提供。 因此,一个工作流的持续活动可能会无限期地延迟其他工作流。 - 没有硬帽。 长脚本、未绑定的记录或故障循环可能会阻止其他可变工作流。
-
取消或进程终止是 停滞的实时工作流的恢复。 等待命令会在一秒后打印状态,并可以停止
Ctrl+C该状态,这会退出130。 - 仅兼容更新的二进制文件合作。 旧
winapp版本早于此功能,并且不协调。 直接调用 UI 自动化 NuGet 包的代码完全超出此保证 - 协调位于 CLI 中,而不是包中。
哪些命令等待轮次:
| Behavior | Commands |
|---|---|
| 并发运行(永不等待) |
status、list-windows、inspect、search、get-property、get-value、get-focused、wait-for |
| 等待轮次,但绝不会占用桌面 |
set-value、、scroll-into-viewscroll --direction/--to、、record |
| 等待轮次,以独占方式获取桌面 |
invoke、click、drag、hover、scroll --wheel、touch、pen、focus、send-keys、screenshot |
中间行是值得理解的行。
set-value, scroll-into-view 并 scroll --direction/--to 驱动 UIA 模式而不是前台,因此它们保持 无外设/锁定会话友好, 并且永远不会阻止任何人使用桌面。 但他们 确实 更改了应用显示的内容,因此他们等待另一个工作流的轮次,而不是编辑字段或从其他人的单击下滚动列表。
在一个工作流中,它们与其他 共享 工作重叠,即捕获 record 正在录制的 set-value 调用。 它们不忽略自己的工作流的前向屏障:同一工作流(a,aclickscreenshot)的早期DesktopExclusive命令仍然阻止它们,就像它阻止每个以后的命令一样,所以单击后跟它的变化会保持你编写的顺序。
screenshot 始终排队进行独占轮次。 并非每个捕获都会干扰桌面(通过 Windows 图形捕获捕获捕获捕获的普通可见窗口),但如果目标最小化,引擎会还原目标,并在帧捕获不可用或--capture-screen读取实时屏幕时回退到前台。 当捕获正在进行时,这些只需要图面,因此命令会提前显示,而不是猜测。 当它将多个窗口组合在一个独占轮次下时,捕获它们,因此保存的图像是一个一致的时刻,而不是前后的混合。 在桌面发布后对文件进行编码和写入。
--capture-screen只需一个窗口。 实时屏幕捕获记录任何实际在前面,只有一个窗口。 显式-w <hwnd>选择一个窗口时,它正好提供了一个区域, 该窗口边界内的像素,包括其顶部的任何对话框或覆盖,这是第一个读取屏幕的原因。 当-a与多个顶级或拥有的窗口匹配时,没有这样的选择,因此命令在捕获任何内容之前失败invalid_arguments,而不是与前台作战。 运行winapp ui list-windows -a <app>并重试-w <hwnd>,或者从其自己的内容中删除--capture-screen以复合每个窗口。
record 共享其轮次,因此同一工作流输入可与捕获交织在一起, 这就是记录驱动应用的工作流的方式。 两个注意事项:
-
record没有工作流 ID 的所有者是一次性所有者,因此它会在整个持续时间内阻止所有其他工作流。 若要同时记录并单击,请为这两个命令指定相同的WINAPP_UI_WORKFLOW_ID命令。 - 在没有帧捕获支持的主机上,录制会回退到 PrintWindow,其空白帧恢复随时可以前台显示窗口。 桌面用于 整个 录制,命令在输出中显示:即使是同一工作流输入也会等待。
你可能会看到的错误: invalid_ui_workflow_id (变量设置为空或超过 256 个字符), desktop_coordination_unavailable (协调状态不可读,无法安全重建或由较新的 winapp工作流编写), queue_capacity_exceeded ( 来自其他 工作流的 64 个命令已等待 — 限制计数实时外部服务员,而不是已启动的进程,因此属于已退出或已终止的命令的条目不占用槽, 和你自己的工作流的命令队列相互隐藏,而不是违反此限制, ui_turn_busy (yield 虽然你自己的工作流仍然运行命令),和 cancelled (在等待时按 Ctrl+C,退出代码 130)。
提前释放轮次: winapp ui yield
四秒宽限是 回退:当无法确定你已完成时,它保留桌面保留。 当你 这样 说时, 说出来 — 立即 yield 将桌面交给桌面, 而不是让其他人等待一个不需要的优雅。
$env:WINAPP_UI_WORKFLOW_ID = [guid]::NewGuid().ToString()
winapp ui invoke File -a notepad
winapp ui click "Save As..." -a notepad
winapp ui set-value txt-filename-a1b2 "notes.txt" -a notepad
winapp ui yield # done — a waiting workflow starts now, not in four seconds
- 一次性命令根本不应设置工作流 ID。 没有一个命令,每个命令在桌面完成的那一刻就释放了桌面,没有什么可产生。
- 多步骤工作流在完成时应产生,尤其是在其他工作流可能正在等待时。 它花费一个快速命令,并从其他人中删除四秒的摊位。
- 这是 幂等的。 产生两次,或在宽限已失效后,成功并报告
{ "released": false }- 这是脚本的正常结束,而不是失败。 - 它 永远不会释放另一个工作流的轮次。 如果其他人持有桌面,或者没有人这样做,这是一个 no-op。
-
ui_turn_busy如果你自己的工作流仍有运行或排队的命令,则失败 , 例如录制。 释放在下方,将桌面从中命令中释放,因此不会释放任何内容,并且正在运行的命令不受影响。 等待它或停止它,然后再次产生。 - 它需要
WINAPP_UI_WORKFLOW_ID。 如果没有一个,它就失败了invalid_arguments。 - 它不需要应用,也没有选择器:它可返回预留,而不是窗口,因此它在应用关闭后仍有效。
等待命令由谁释放桌面而不是轮询来唤醒,因此队列在等待和交接时几乎没有什么费用。 每个服务员偶尔也会重新检查,这是在进程被终止时恢复桌面的功能,永远不会发布任何内容:队列头上的命令每隔半秒显示一次,后面还有命令(无论如何,在头部执行操作之前都无法运行)。
目标应用
按进程名称
winapp ui inspect -a notepad
winapp ui inspect -a slack # auto-picks visible window for multi-process apps
winapp ui inspect -a imageresizer # partial match: finds PowerToys.ImageResizer
按窗口标题
winapp ui inspect -a "LICENSE - Notepad"
winapp ui inspect -a "Fix WinApp" # partial title match
按 PID
winapp ui inspect -a 12345
按 HWND (稳定 — 在选项卡/标题更改中幸存下来)
# Discover HWNDs
winapp ui list-windows -a Terminal
→ HWND 985238: "🤖 Testing" (WindowsTerminal, PID 21228)
→ HWND 131906: "Fix WinApp" (WindowsTerminal, PID 21228)
# Target specific window
winapp ui inspect -w 131906
winapp ui screenshot -w 131906
用于 -a 发现, -w 用于稳定目标。 匹配多个窗口时 -a ,命令会用 HWND 列出这些窗口供你选择。
选择器
使用检查/搜索输出中显示的 [brackets] 选择器的目标元素。
有三种类型的选择器:
| Selector | 含义 | Example |
|---|---|---|
MinimizeButton |
AutomationId (在唯一时显示 — 稳定,首选) | winapp ui invoke MinimizeButton -a myapp |
btn-close-d1a0 |
语义 slug (当没有唯一的 AutomationId 时显示) | winapp ui invoke btn-close-d1a0 -a myapp |
Submit |
针对 Name/AutomationId 的纯文本搜索(不区分大小写的子字符串) | winapp ui invoke Submit -a myapp |
AutomationId 选择器 是开发人员集标识符(AutomationProperties.AutomationId 在 XAML 中)。
当 AutomationId 在整个 UI 树中是唯一的, inspect 并 search 直接将其显示为选择器时, 这些操作在布局更改、本地化和树结构调整中幸存下来。
当不存在唯一的 AutomationId 时,将生成 btn-close-d1a0(例如)。
格式: prefix-name-hash. 哈希验证元素标识,但在 UI 更改后可能会过时。
检查输出格式
该 inspect 命令显示带有彩色输出的元素树(以青绿色表示选择器,名称为绿色,元数据为灰色):
TabView Tab (0,-1 1200x48)
TabListView List (4,-1 1100x48)
tab-newtab-5f5b TabItem "New Tab" (14,-1 200x48)
NewTabButton SplitButton "New Tab" [collapsed] (1104,5 96x36)
Found 10 elements (--depth 3). Use the first token as selector, e.g.: winapp ui invoke TabView -a terminal
每行 的第一个单词 是选择器 , 将其与其他命令一起使用 ui 。
当元素具有唯一的 AutomationId 时,它直接使用(例如,TabViewNewTabButton)。
当不存在唯一的 AutomationId 时,将使用生成的 slug(例如)。 tab-newtab-5f5b
语义污点
Slugs 使用格式:其中: prefix-normalizedname-hash
- prefix — 3 字母类型缩写(btn、txt、chk、cmb、itm、tab、img、lbl、pn、win、grp、lnk、mnu 等)
- normalizedname — AutomationId(首选)或 Name 中的小写字母数字,最大 15 个字符
- hash - 元素 RuntimeId 的 4 个字符哈希(验证元素标识)
Slugs 是 shell 安全(无特殊字符)、唯一的,可以直接用作参数。 如果没有查询筛选器,哈希将提供过期检测 - 如果元素已被替换,则会收到:“元素可能已更改。 重新运行检查。”有关筛选的查询,请参阅 作用域查询和类型化查询。
无名称或 AutomationId 的元素仅显示前缀 + 哈希(例如 pn-c8a3)。
消除多个匹配项的歧义
输出中的 Slugs 是唯一的 inspect/search ,但可以在布局更改之间更改 - 在多个匹配项时,在纯类型名称或文本上使用它们。 当选择器不明确时,CLI 会打印所有匹配项及其污点,以便你可以选取正确的匹配项,并使用该 slug 重新运行。
winapp ui search Button -a myapp # shows: btn-ok-a1b2 "OK", btn-cancel-c3d4 "Cancel"
winapp ui invoke btn-ok-a1b2 -a myapp # invoke using slug (preferred)
winapp ui invoke btn-cancel-c3d4 -a myapp # invoke the other Button by its slug
纯文本搜索
使用纯文本搜索元素 - 无需特殊语法:
winapp ui search Minimize -a notepad # finds elements with "Minimize" in Name or AutomationId
winapp ui search Close -a notepad # case-insensitive substring match
winapp ui invoke Minimize -a notepad # search + invoke in one step (disambiguates if needed)
winapp ui search "Save" -a notepad # find elements containing "Save"
winapp ui search "error" -a myapp # case-insensitive match
当文本搜索匹配多个元素(例如,SettingsExpander,其中组、按钮和文本)共享相同的名称时,CLI 会自动选取唯一可调用的元素。 如果多个是可调用的,它将列出所有匹配项与 slugs。
对于不可调用的搜索结果(例如 Button 内的 TextBlock),搜索会自动显示最接近 的可调用上级 ,即可用于 invoke的父元素。
这适用于所有搜索选择器:
lbl-savechanges-a1b2 "Save changes" (120,40 80x20)
^ invoke via: btn-save-c3d4 "Save"
可以直接使用图面选择器:
winapp ui invoke btn-save-c3d4 -a myapp # invoke the parent Button
Commands
状态
连接到应用并显示连接信息。
winapp ui status -a notepad
winapp ui status -a notepad --json
检查
查看 UI 元素树。 输出显示层次结构的 2 空间缩进的语义污点:
winapp ui inspect -a notepad # full window tree, depth 3
winapp ui inspect -a notepad --depth 5 # deeper tree
winapp ui inspect txt-searchbox-e5f6 -a notepad # subtree rooted at element
winapp ui inspect --ancestors btn-close-d1a2 -a notepad # walk up from element to root
winapp ui inspect -a myapp --interactive # invokable elements only, auto-depth 8
winapp ui inspect -a myapp --hide-disabled # hide disabled elements
winapp ui inspect -a myapp --hide-offscreen # hide offscreen elements
示例输出(默认值):
win-aidevgalleryp-f1a3 "AI Dev Gallery Preview" (94,206 1280x1023)
pn-c8a3 (102,207 1264x1014)
btn-minimize-d1a0 "Minimize" (1222,206 48x48)
btn-maximize-e2b1 "Maximize" (1270,206 48x48)
itm-samples-3f2c "Samples" (102,330 72x62)
示例输出(--interactive — 仅可调用元素,平面列表):
btn-minimize-d1a0 "Minimize" (1222,206 48x48)
btn-maximize-e2b1 "Maximize" (1270,206 48x48)
btn-close-d1a2 "Close" (1318,206 48x48)
itm-home-7b3e "Home" (102,268 72x62)
itm-samples-3f2c "Samples" (102,330 72x62)
itm-models-9a4f "Models" (102,392 72x62)
元素可能显示以下状态标记:
-
[on]/[off]/[indeterminate]— 切换/复选框状态 -
[collapsed]/[expanded]— 树、组合框、菜单项的展开/折叠状态 -
[scroll:v]/[scroll:h]/[scroll:vh]— 可滚动容器(垂直、水平或两者兼有) -
[offscreen]— 元素在屏幕上不可见 -
[disabled]- 未启用元素 -
value="..."— 可编辑元素的当前文本内容(与名称不同时)
搜索
查找与选择器匹配的元素。 输出显示语义污点:
winapp ui search Button -a notepad # all buttons
winapp ui search Close -a notepad # finds elements with "Close" in name
winapp ui search SearchBox -a notepad # finds elements with "SearchBox" in name or AutomationId
winapp ui search Button --max 10 -a notepad # limit results
示例输出:
btn-minimize-d1a0 "Minimize" (1222,206 48x48)
btn-maximize-e2b1 "Maximize" (1270,206 48x48)
btn-close-d1a2 "Close" (1318,206 48x48)
输出中显示的 Slugs(例如, btn-minimize-d1a0)可以直接与其他命令一起使用:
winapp ui invoke btn-minimize-d1a0 -a notepad
get-property
从元素读取属性值。 包括模式特定的状态(ToggleState、Value、IsSelected 等)。
winapp ui get-property btn-submit-7a90 -a myapp # all properties
winapp ui get-property chk-checkbox-b2c3 -p ToggleState -a myapp # checkbox state
winapp ui get-property txt-textbox-a4b1 -p Value -a myapp # current text value
winapp ui get-property cmb-combobox-d5e6 -p ExpandCollapseState -a myapp # expanded or collapsed
winapp ui get-property Document -p FontWeight -a myapp --json # document formatting
属性名区分大小写。 未知名称失败, invalid_arguments 如下所示 --json;省略 --property 以列出属性,包括下面的所有六个文本格式属性。
wait-for --property 使用相同的区分大小写的名称,并在轮询之前拒绝未知名称。
全文档文本格式
格式设置在元素的整个 TextPattern 文档中读取,而不是其当前选定内容或插入符号。 读取不会更改焦点或选择。
| 财产 | 统一值(以字符串的形式返回) |
|---|---|
FontWeight |
数字权重,如 "400" (普通)或 "700" (粗体) |
FontName |
字体系列名称,例如 "Courier New" |
FontSize |
大小(以磅为单位),例如 "15.5" |
ForegroundColor |
十进制Windows COLORREF (0x00BBGGRR),例如 "3678732" RGB(12、34、56) |
IsItalic |
"True" 或 "False" |
StrikethroughStyle |
数字 UIA 文本修饰样式,例如 "0" (无)或 "1" (单) |
数字使用固定格式(不考虑区域设置的小数点)。 每个属性可以改为返回:
| 值 | 含义和下一步 |
|---|---|
"Mixed" |
文档内的格式各不相同。 不要将其视为统一值;此命令不查询单个文本范围。 |
"NotSupported" |
文档的 TextPattern 提供程序不报告此属性。 检查应用的辅助功能支持。 |
"Unavailable" |
元素没有 TextPattern。 使用 inspect 或 search 查找其文本/文档元素。 |
列出所有属性时,如果无法解析任何实时元素,则缓存的基本属性将保持可用,并且省略格式不正确的格式值而不放弃其他属性。 这些遗漏记录为警告。 请求特定的格式设置属性以获取错误而不是遗漏。
提供程序失败仍然是错误,而不是 "Unavailable"。 对于 stale_element,请再次检查应用,并使用当前选择器重试。
JSON 信封包括elementId类型化element和字符串值properties。
现有属性(包括 BoundingRectangle)保留其格式。
例如,格式 properties 设置部分为:
{
"FontWeight": "700"
}
屏幕截图
将窗口或元素捕获为 PNG。
winapp ui screenshot -a notepad # saves screenshot.png in cwd
winapp ui screenshot -a notepad --output my.png # custom filename
winapp ui screenshot --quiet -a notepad -o my.png # save without informational output
winapp ui screenshot -a notepad --json # returns file path as JSON
winapp ui screenshot -w 131906 # target specific HWND (+ its dialogs)
winapp ui screenshot txt-searchbox-e5f6 -a myapp # crop to element bounds
winapp ui screenshot -w 131906 --capture-screen # one screen region, with visible overlays in place; foregrounds window
winapp ui screenshot -a myapp --focus # bring window to foreground first, then capture (default WGC path)
如果没有元素选择器,默认捕获会将多个窗口合并为 一个标记的并行复合 PNG,而不是单独的文件。
-a 按进程名称或 PID 包括应用的窗口及其拥有的窗口。 基于 -a 游戏的匹配选择一个匹配窗口及其拥有的窗口; -w 显式选择一个窗口及其拥有的窗口,而不是进程中的每个窗口。 因此,即使显式选择主 HWND,拥有的对话或工具提示也可以显示为自己的面板。 元素选择器裁剪到该元素,而不是撰写窗口。
--quiet 取消单窗口捕获和复合捕获的信息输出,包括保存的路径。 警告和捕获失败诊断仍可见。 需要将文件路径和维度用作结构化输出时,请 --json 改用。
使用 --on sandbox, --output 将主机目标命名。 成功输出并 --json 报告映像传递后主机路径。
默认捕获路径使用Windows。Graphics.Capture (WGC),读取实际的 DWM 复合表面 - 保留圆角、透明度和工作,即使窗口被其他windows遮挡也是如此。 如果 WGC 不可用(较旧的Windows内部版本),CLI 将回退到 PrintWindow。
需要在屏幕上位置显示弹出窗口或工具提示(包括目标窗口不拥有的覆盖层)时使用 --capture-screen -w <hwnd> 。 它读取该窗口的屏幕区域,而不是撰写已标记面板,并将窗口置于前台。 使用-a时,它只需要一个匹配窗口;如果有多个顶级或拥有的窗口匹配,请使用winapp ui list-windows -a <app>并重试。-w <hwnd>
--focus如果只想在不切换捕获模式的情况下前台显示窗口(例如,确保屏幕截图与用户当前正在查看的内容匹配)。
由于屏幕 DC 捕获了前面实际的任何内容,
--capture-screen因此在捕获目标之前立即到达前台 ,如果目标没有(焦点窃取防护、UAC 提示符或激活自身的另一个窗口),则验证目标是否已到达前台并失败foreground_not_target。 在这种情况下,没有写入任何图像 — 以前命令退出了 0,并传回了错误窗口的图片。ui record --capture-screen在第一帧之前应用相同的检查。
记录
将窗口或元素区域记录到 H.264 MP4。 对于无人参与的脚本,首选正 --duration-sec 值。 如果没有持续时间,录制将继续,直到 Ctrl+C 或重定向 stdin、换行符或 EOF。 npm uiRecord 和 targetRecord 帮助程序需要一个介于 1 到 86400 的整数 durationSec ;其中止信号会强制取消,而不是正常完成录制。
# Record for 10 seconds
winapp ui record -a myapp --duration-sec 10 --fps 15 --output demo.mp4
# Add agent-readable frames
winapp ui record -a myapp --frames --duration-sec 10 --fps 10 --output evidence.mp4 --json
# Stop an unbounded recording through stdin
"" | winapp ui record -a myapp --json --output capture.mp4
# Include screen overlays and popups
winapp ui record -a myapp --capture-screen --duration-sec 5 --output with-popups.mp4
选项:
-
--duration-sec N— 记录 N 秒。 默认 0 条记录,直到停止。 -
--fps N— 每秒目标帧数(默认值 15)。 -
--max-edge N— 向下缩放,因此最长的边缘最多为 N 像素(0 = 无下刻度)。 -
--capture-screen— 从屏幕 DC 捕获(包括覆盖层/弹出窗口;前台窗口)。 -
--output <path>— 输出 MP4 路径。 默认值为recording-<timestamp>-<guid>.mp4. -
--overwrite— 在新拍摄完成后替换现有录制输出。 如果没有它,则拒绝现有输出。 -
--frames— 将带时间戳的 JPEG 证据写入到<output-name>.frames。 支持 1-30 fps 和--max-edge64-4096(默认值 1280)。 帧数据上限为 1 GiB;如果达到上限,MP4 将继续。
代理可读帧项目:
evidence.mp4
evidence.frames/
manifest.json
frames.ndjson
frames/
frame-000000-t000000000012.jpg
frames.ndjson每个样本有sampleIndex一行,单调elapsedMs、MP4 相对mediaTimeMs、imageIndex和filechanged。 连续像素相同的样本重复使用上述质量-85 JPEG。
manifest.json 记录请求、计时、MP4 状态、图像维度和状态(complete或 partial) truncated。 截断计时涵盖保留的前缀,同时 video 介绍了完整的 MP4。
选择新的输出路径,除非你打算将录制替换为 --overwrite。
如果没有它,现有的视频或其配对 .frames 的目录会阻止录制,即使省略 --frames。
如果新捕获失败,以前的 MP4 将保持不变。 成功替换后,即使新录制省略--frames,上一帧目录也会保留为<output-name>.frames.previous-<id>。 保留部分证据,并在重试之前遵循报告 recoveryHint 。 如果 MP4 最终化失败,则可以在以下下 <output-name>.frames.partial-*发布保留的帧。 帧项目包含未加密的屏幕内容;像屏幕截图或视频一样处理它们。
使用 时 --on sandbox,MP4 和帧目录都传送到主机,包括省略时 --output 的默认输出。 有关中断的录制和整个桌面捕获,请参阅 沙盒捕获 。
捕获模式 (在 JSON mode 字段中报告):
-
wgc— Windows图形捕获(默认值;在窗口被遮挡时有效)。 -
printwindow— GDI PrintWindow (当 WGC 在此系统/会话上不可用时回退;请重新运行--capture-screen以使用屏幕 DC)。 -
screen— 通过--capture-screen屏幕 DC(包括覆盖/弹出窗口;将窗口引入前台)。
JSON 输出(--json):
-
stdout: 最终记录结果,包括节奏、停止原因、可选
frameArtifacts和警告。 -
stderr: 每行一个
recording-startedJSON 对象:第一帧之后的事件,如果稍后录制失败,则后跟错误。 仅当帧输出处于活动状态时,才包含帧路径。
错误代码:
-
element_not_found— 选择器不匹配。 -
ambiguous_selector— 选择器匹配多个元素;使用建议的滑行。 -
invalid_arguments— 选项值无效。 -
output_exists— 录制输出已存在,不能在请求的选项下替换。 -
frame_output_failed— 在帧输出失败后,两个项目都无法保留。 -
partial_output— 仅完成一个项目;检查partialOutput和recoveryHint。
已知限制: 在窗口弹出窗口中录制元素可能会捕获基础窗口。 记录整个窗口,或按照静止图像的 屏幕截图覆盖工作流 进行操作。 请参阅 #646。
调用
winapp ui invoke SettingsCategory -a myapp --action select
winapp ui invoke AgreeCheckbox -a myapp --action toggle-on --json
winapp ui invoke SizeComboBox -a myapp --action expand
winapp ui invoke SubmitButton -a myapp
当测试必须对所选元素执行特定操作时使用 --action 。 它永远不会尝试另一个模式或可调用的上级,即使请求的操作失败。 将使用 选择支持调用和选择的控件, --action select而不是调用控件。 使用--action时,slug 正好以一个元素为目标;与多个元素匹配的纯文本或 AutomationId 选择器会失败并用非零退出代码关闭,而不是在第一个匹配项上执行操作,因此在名称不明确时传递数据。inspect/search
| Action | 运算 |
|---|---|
invoke |
InvokePattern.Invoke |
select |
SelectionItemPattern.Select |
toggle |
TogglePattern.Toggle,恰好一次 |
toggle-on / toggle-off |
读取 ToggleState;如果不更改已正确状态,则成功,否则切换并验证 |
expand / collapse |
ExpandCollapsePattern.Expand / Collapse |
对于 toggle-on 和 toggle-off,起始 Indeterminate 状态最多允许两个转换,在每个转换后检查状态。 其他起始状态允许一次转换。 如果未达到请求的状态,命令将失败,而不是继续切换。 验证失败可能会使控件更改;在决定下一步操作之前阅读 ToggleState 。
如果没有 --action,现有自动行为保持不变:根据需要尝试 InvokePattern、TogglePattern、SelectionItemPattern、ExpandCollapsePattern(expand),并根据需要重试可调用的-上级重试。
不支持的操作失败并出现非零退出代码,并且 --jsonstderr 上出现结构化错误。 检查所选控件并选择它支持的操作,或显式面向预期的父级。 成功 JSON 包括 requestedAction 并 performedAction查看 JSON 参考。
click
使用鼠标模拟单击其屏幕坐标处的元素。 对不支持 InvokePattern 的控件(例如列标题、列表项)使用此选项。
winapp ui click btn-column1-a3f2 -a myapp # single click by slug
winapp ui click "Column1" -a myapp # single click by text search
winapp ui click btn-column1-a3f2 -a myapp --double # double-click
winapp ui click btn-column1-a3f2 -a myapp --right # right-click
与其他输入注入谓词一样,
click将目标引入前台并 快速失败 (no_interactive_desktop在锁定/安全的桌面上,foreground_not_target如果无法传输焦点),而不是单击错误的窗口。 它还 将重新解析按钮关闭前的元素:定位光标后,它会进行最后一个位置检查,因此,在单击进入空白空间后,连续移动/动画目标将失败target_moved,而不是报告成功, 报告的成功意味着当按钮关闭时,目标仍处于原位。
拖动
在一个点按鼠标按钮,移动到另一个终结点,然后释放, drag <from> <to>其中每个终结点是 元素选择器 (从/拖到元素的中心)或 屏幕坐标 x,y 完全如所报告的那样 winapp ui inspect。 自由混合和匹配(选择器→elector、selector→coords、coords→coords)。
与 SendInput 中间移动一起使用,以便应用看到真实的消息流 WM_MOUSEMOVE 。 使用它对手柄、滑块、画布绘图和拖放进行重新排序/调整大小。
winapp ui drag itm-card-9f8e itm-slot-2c1a -a myapp # reorder: card center → slot center
winapp ui drag itm-card-9f8e 300,400 -a myapp # element center → screen coords (from inspect)
winapp ui drag 120,200 480,200 -a myapp # raw screen coords → screen coords
winapp ui drag itm-card-9f8e itm-trash-0001 -a myapp --right # right-button drag
# Press-and-hold / long-press and drop-target dwell
winapp ui drag tile-photo-7b3c tile-photo-7b3c -a myapp --hold-ms 600 # long-press: from == to, hold 600ms, no move
winapp ui drag itm-card-9f8e pane-left-2c1a -a myapp --dwell-ms 350 # settle on the drop target before releasing
选项:
-
--right— 使用鼠标右键(而不是左键)拖动。 -
--hold-ms <ms>— 在移动之前,在开始处按住按钮(默认值:0)。 如果没有<from> == <to>移动,这将执行 按下和按住/长按 手势。 -
--dwell-ms <ms>— 在移动后停留在目的地之前(默认值:0)。 允许从持续悬停(而不是光标到达的瞬间)将手臂从持续悬停(而不是光标到达的瞬间)闩锁 上放置目标/合并覆盖 。
裸体
x,y是同一空间winapp ui inspect/search报表中的屏幕坐标,选择器解析为元素中心 - 首先检查以选取点。
同样
send-keys --via send-input,在将目标引入前台后,drag在屏幕坐标处注入 OS 范围。 如果无法将焦点带到目标(例如从后台进程中窃取焦点保护 ),命令会失败(foreground_not_target而不是 拖到错误的窗口上), 焦点或首先单击窗口。 在锁定/安全桌面上,它失败并出现no_interactive_desktop。 拖动 前立即重新解析每个元素终结点;如果它仍在移动/调整大小(动画目标),则命令会失败,target_moved而不是拖到过时点。 (无法重新验证裸x,y终结点,因此它们 as-is使用。
触摸
使用Windows指针注入 API 注入合成触摸手势。 联系人定位点是 元素选择器 (使用元素的中心)或显式 屏幕坐标 x,y ( --at 相同的空间 winapp ui inspect 报告)。 将其用于点击/按鼠标模拟无法表达的多点触控手势。
winapp ui touch btn-ok-1a2b -a myapp # tap at the element center
winapp ui touch -a myapp --at 320,240 # tap at explicit screen coords
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 # pinch-to-zoom out (2 fingers)
winapp ui touch img-map-9f8e -a myapp --gesture stretch --distance 200 # stretch-to-zoom in (2 fingers)
选项:
-
--gesture <g>—tap(default)、double-tap、、long-pressswipe、pinch。stretch -
--at <x,y>— 显式起点(屏幕坐标)。 默认为选择器的元素中心。 -
--to-point <x,y>— aswipe. 的终点。 优先于--direction. -
--direction <right|left|up|down>— 轻扫方向 (默认值:right) 。 结合在一起--distance,在未提供终结点时--to-point计算终结点。 -
--distance <px>— 手指分布,pinch/stretch或轻扫距离(以像素为单位)。 -
--hold-ms <ms>— 在解除之前按住联系人(长按保持时间;在未设置时默认为 500 毫秒long-press)。 -
--duration-ms <ms>— 移动手势的滑行时间(轻扫/收缩/拉伸;默认为 300)。 -
--fingers <n>— 联系人数(1-10;默认 1)。pinch/stretch始终使用 2。
注射安全。
touch拒绝注入,除非 非零目标窗口句柄 解析,并且该窗口保留前台 ,否则no_target当无法解析窗口、foreground_not_target无法传输焦点或no_interactive_desktop锁定/安全桌面时失败。 每个坐标(元素中心、显式--at/--to-point和生成的路点)都会针对目标窗口矩形进行检查;窗口外的一个点显示为非致命警告(warnings[]文本模式下的条目--json或警告行),并且注入仍在继续 -- 匹配鼠标谓词(click/drag/hover/scroll),这也在窗口外坐标处注入。--fingers高于 10 的前面被拒绝。硬件说明。 触摸更喜欢新式合成指针设备(
CreateSyntheticPointerDevice(PT_TOUCH))并回退到旧InitializeTouchInjection/InjectTouchInput版 API。 如果当前设备/会话不支持注入,则命令会显示 实际的 Win32 错误代码 (例如“unsupported”),而不是报告错误成功 , 将非零退出视为“未传递触摸”。远程桌面/VM 会话。 在远程桌面(RDP)或某些 VM 会话中,OS 可能会接受合成触摸(退出 0),而不会真正到达目标应用。 检测到远程会话时,
touch追加 传递不确定性警告 (warnings[]输入或--json文本模式下的警告行)。 ✅然后,/exit 0 表示注入调用成功,而不是应用收到输入;确认其效果ui screenshot/ui inspect何时生效。
笔
使用合成笔/触笔 API 注入合成笔/触笔输入(点击和墨迹笔划)Windows合成指针 API(CreateSyntheticPointerDevice(PT_PEN);Windows 10 1809+)。 以元素中心、显式 --at 点或完整 --path 墨迹笔划为目标。
winapp ui pen canvas-1a2b -a myapp # pen tap at the element center
winapp ui pen -a myapp --at 320,240 --pressure 0.8 # firm pen tap at explicit coords
winapp ui pen -a myapp --path "100,100 150,120 210,140 260,120" # draw an ink stroke
winapp ui pen -a myapp --path "100,100 260,100" --eraser # erase along a stroke
winapp ui pen -a myapp --at 200,200 --tilt-x 30 --tilt-y -15 # tilted pen contact
选项:
-
--at <x,y>— 笔接触点(屏幕坐标)。 默认为选择器的元素中心。 给定时--path忽略。 -
--path "<x,y x,y …>"— 墨迹笔划路径作为空格分隔x,y的对(一个点路径是点击)。 -
--pressure <0.0–1.0>— 笔压(默认值 0.5)。 -
--tilt-x <deg>/--tilt-y <deg>— 笔倾斜角度,\90 到 90(默认值 0)。 -
--eraser- 使用笔的橡皮擦端而不是小费。 -
--duration-ms <ms>— 总笔划行程时间(以毫秒为单位)分布为跨路径的内插 UPDATE 帧(默认值:每路点约 10 毫秒)。 使用此控件控制笔明显从头到尾移动的速度。
注射安全。 同样
touch,pen拒绝在没有非零的前台目标窗口(no_target/foreground_not_target/no_interactive_desktop)的情况下注入,并针对目标窗口矩形检查每个墨迹点,将任何窗口外坐标显示为非致命警告(warnings[]在--json文本模式下或警告行中)同时仍注入 - 与鼠标谓词一致。 无效--pressure(在 0.0–1.0 之外)或倾斜(±90°外部)会提前被拒绝。远程桌面/VM 会话。 笔路由对于远程桌面尤其不可靠:注入调用可以报告成功(退出 0),而没有笔输入到达应用。 检测到远程会话时,
pen追加 传递不确定性警告 (warnings[]在--json文本模式下或警告行中),以免 ✅ 误认为已确认的传递。 在本地 交互式桌面上验证依赖于笔的流。
悬停
将鼠标移动到元素的中心以触发悬停效果(工具提示、浮出控件、视觉状态)。 用于 SendInput 具有小摇摆的逼真的鼠标移动,然后等待可配置的停留时间。
winapp ui hover btn-info-a1b2 -a myapp # hover with default 800ms dwell
winapp ui hover btn-info-a1b2 -a myapp --dwell-time 1200 # longer dwell for slow tooltips
winapp ui list-windows -a myapp # use the main window's HWND below
winapp ui hover btn-info-a1b2 -a myapp; winapp ui screenshot -w <hwnd> --capture-screen # hover then capture tooltip in place
选项:
-
--dwell-time <ms>— 将鼠标悬停后等待的时间(默认值:800,范围:0-10000)
send-keys
将合成键盘输入 - 与键盘相对应的键盘输入 click。 UIA 没有键盘注入模式,因此这会下降到 Win32 层。 将其用于键盘导航(箭头、Tab、Enter、Esc)、快捷方式(ctrl+c, alt+f4以及键入需要按键击事件的控件,而不是 set-value原子写入)。
winapp ui send-keys "down down enter" -a myapp # arrow navigation then commit
winapp ui send-keys "ctrl+a delete" -a myapp # select all, then delete
winapp ui send-keys "Hello world" --target txt-name-a1b2 -a myapp # focus a field, then type text
winapp ui send-keys "text=down text=down text=enter" -a myapp # type the words, don't press the keys
winapp ui send-keys "down down enter" -a myapp --verbatim # same, but type the whole argument literally
winapp ui send-keys "alt+f4" -a myapp # close window via accelerator
winapp ui send-keys "vk=0x5D" -a myapp # a key with no friendly name (Apps/Menu key)
winapp ui send-keys "ctrl+shift+t" -a myapp --via send-input # use OS-wide injection instead of PostMessage
winapp ui send-keys "win+shift+v" -a myapp --via send-input --allow-system-keys # opt in to drive a global hotkey
密钥语法 (空格分隔的标记,引用多标记字符串):
-
命名键 -
enter/return、、tab、、esc/escapespacebackspacedelete/delinserthomeendpageup/pguppagedown/pgdnup/down/left/rightf1-f16、apps、 。printscreencapslock -
序列 - 按顺序按下多个标记:
down down enter。 -
修饰符组合 -
ctrl, ,shiftaltwin联接 :+ctrl+shift+t, 。alt+f4 -
文本文本 - 不是已知键的任何标记都按字符键入:
hello。 相邻的文字单词将保留两者之间的空间,因此带引号的短语类似于"Hello world"键入逐字(保留空格);仅包含+C++a+b文本的文本,而不是作为组合分析。 -
显式文本转义 - 为标记
text=添加前缀,以逐字键入,即使它与键或修饰符名称相撞:text=enter键入单词“Enter”而不是按 Enter,并text=ctrl+a键入文本字符串。vk=镜像转义;转义值仍与相邻文字单词text=down low(→“低”)合并。 由于标记是空格拆分(以及相邻文本与单个空间重新联接),因此在 值内text=使用反斜杠转义 来键入不会生存的空格:\s→空格、\t→制表符、\n→换行符、\r→换行符、\\→文本反斜杠。\n、\r和\r\n每个插入一个换行符(Enter/VK_RETURN),因此text=line1\nline2,text=line1\r\nline2两者都键入一个新行。 因此text=a\s\sb,键入“a b”(双空格),并text=\shi保留前导空间。 无法识别的转义(例如\x)是左逐字的。 -
全参数文本 (
--verbatim) - 当整个有效负载为文本文本时,传递--verbatim而不是转义每个标记。text=它按给定(无命名键/组合/vk=/text=解释)完全键入整个键参数,并且与普通路径不同,无需\s保留确切的内部空格(无需折叠)。 因此send-keys "down down enter" --verbatim,键入单词,并send-keys "a b" --verbatim保留双倍空格。 反斜杠转义在模式下--verbatim解码(\s类型为反斜杠和“s”);如果需要转义的控制字符,请使用text=令牌。 -
原始虚拟密钥 (
vk=0xNN十六进制) 或vk=NN(十进制) 用于没有友好名称的键。
选项:
-
--target <selector>— 在发送密钥之前,请关注此元素(通过 UIA)。 如果没有它,键将转到应用的当前重点元素。 -
--verbatim— 键入整个键参数作为文本文本(无键/组合/vk=/text=分析),并保留确切的空格。 每个标记text=转义的整个参数形式。 -
--via <transport>—post-message(默认值)发布到WM_KEYDOWN/WM_KEYUP/WM_CHAR目标窗口的队列。 它以 HWND 为目标并绕过 UIPI(跨完整性级别工作)。send-input通过SendInput注入 OS 范围并转到前台窗口。
选择传输/已知限制:
-
post-message是默认值,因为它绕过 UIPI,并且不依赖于前台的窗口。 限制:它无法触发通过WH_KEYBOARD_LL低级别挂钩注册的全局热键(任何窗口队列上游的点击输入),并且通过GetAsyncKeyState读取原始密钥状态的应用可能无法观察到保留的修饰符。 它在前台后自动解析并发布到目标线程的 焦点子窗口 (通过GetGUIThreadInfo),因此经典 Win32/WinForms 应用,其控件是单独的子窗口接收键,而无需手动定位控件。 WinUI 3/UWP 应用具有无窗口 XAML 控件,没有子 HWND,因此已发布WM_CHAR/WM_KEYDOWN的 XAML 控件无法进入并被删除 - 后消息无法驱动它们(命令警告并退出 0);使用。--via send-input(WPF窗口是单 HWND,将密钥路由到内部聚焦的元素,因此消息后会在那里工作。 -
send-input生成完全真实的输入(对低级别挂钩可见GetAsyncKeyState的修饰符),但转到任何窗口是前台, 并在从提升的进程注入到较低完整性(AppContainer/AppX)目标时被 UIPI 阻止。 如果send-input报告失败,则目标可能提升或 AppX 应用 ( 使用post-message)或在匹配完整性级别运行 CLI。 作为一个安全防护,send-input验证目标窗口实际上是在前台注入和 失败(foreground_not_target)而不是键入错误窗口 (如果无法将焦点带到它 - 焦点或首先单击窗口)。 在 锁定的或安全的桌面上 ,它会失败(no_interactive_desktop不存在要注入的前景窗口)- 解锁会话,或使用 UIA 模式谓词 (set-value,invoke) 。 -
系统保留的组合(
win+l、、、win+rctrl+shift+esc、ctrl+alt+delalt+tab、alt+f4ctrl+esc、孤独win/printscreen、...)在 OS/shell 上执行操作,而不是仅当发送 OS 范围时的目标。send-input默认情况下,拒绝它们 (错误和invalid_arguments发送任何内容),因为在 OS 级别注入它们的效果远远超出了目标窗口(例如win+l锁定会话)。 传递--allow-system-keys以选择加入 — 这允许你驱动全局热键(例如 PowerToys 或win+shift+vwin+r(全局低级别挂钩监视 OS 范围的输入流,因此注入的组合会激发它)。 即使--allow-system-keys阻止了异常:win+l锁定工作站,该工作站LockWorkStation()无法从自动化(中断 CI 和远程桌面会话)中恢复,并且ctrl+alt+del是一个安全注意序列(SAS),它Windows从注入的输入中删除而不考虑标志 - 它永远无法生效,因此它出错(invalid_arguments退出 1),而不是报告误导性的成功。 其他组合(alt+f4、、ctrl+shift+escwin+r...)通过标志(调用方小心)变得允许。 或者,若要将系统组合传递到特定窗口使用--via post-message,即窗口范围和不受影响(发布是无害的,尽管已发布win+lalt+f4的仍然关闭目标窗口)。
按键击事件(KeyDown/TextChanged):
-
命名的键和修饰符组合(
down、、enterctrl+shift+t)vk=0xNN在KeyDown种传输上触发真实KeyUp(和)-它们作为离散WM_KEYDOWN/WM_KEYUP(或SendInput虚拟键事件)传递。 -
文本类型化文本 (
hello) 因传输而异:-
--via send-input将每个字符映射到活动键盘布局上的虚拟键(加上 Shift),因此目标会看到一个正版KeyDown的虚拟键 ,后跟 OS 组合WM_CHAR(提升TextChanged)(即每个字符一个完整的击键)。 在当前布局(或需要 Ctrl/AltGr)上无法访问的字符回退到 Unicode 数据包,以便确切的字符仍可到达。 如果需要每击键send-input保真度(例如驱动 WinUI 3/WPF其处理程序关闭),请使用KeyDown它。TextBoxKeyDown对于普通的(非提升)WinUI 3 测试主机,请首先将其窗口置于前台(winapp ui focus/单击该窗口),因为它send-input面向前台窗口。 -
--via post-message每个字符发布一WM_CHAR个字符(它不WM_KEYDOWN/WM_KEYUP发布类型化文本 - 这些为命名键/组合保留),这不会引发每个字符KeyDown。 它会自动将目标重新定位到窗口的 焦点子控件,因此经典 Win32/WinFormsWM_CHAR驱动的编辑控件会将文本(引发TextChanged)。 注意事项:WinUI 3/UWP/XAML 应用(winapp 的主要目标)具有忽略已发布WM_CHAR/的WM_KEYDOWN控件,因此文本文本和命名键(Enter、数字、...)都无法访问它们,即使命令报告成功。 当目标看起来像 XAML 并且仍然退出 0 时,它会发出警告(PostMessage是触发和忘记且无法确认传递)。 用于--via send-input驱动 WinUI 3/UWP/WPF 应用;保留post-message经典 Win32 控件,或者仅当需要跨完整性级别划分窗口范围的控件时。
-
JSON 输出(--json):结果hwnd是将键传递到的有效窗口,这是--via post-message命令重定向到它的解析焦点子控件(不一定是顶级-w/-a/-e窗口),因此自动化可以准确确认输入到达的位置。 当该有效目标看起来像无窗口 XAML 主机时,上面的传递注意事项也显示为 warnings[] 条目(控制台中显示的相同公告),因此 ✅ 退出 0 不会误认为是确认的传递。
set-value
以编程方式在可编辑元素上设置值(无击键,无应用前台)。 使用回退链:
- ValuePattern — TextBox、ComboBox、PasswordBox 和大多数可编辑控件。
- RangeValuePattern — 当值分析为数字时,数值控件(滑块、ProgressBar)。
-
LegacyIAccessible (
IAccessible::put_accValue) - 仅 TextPattern 的编辑控件的回退,这些控件不公开 ValuePattern(例如 rich-edit/Documentcompose 框)。 这将缩小读取/写入差距,其中get-value可以读取此类控件,但set-value无法读取。
winapp ui set-value txt-textbox-a4b1 "Hello world" -a notepad
winapp ui set-value sld-volume-b2c3 75 -a myapp
winapp ui set-value doc-compose-9f3a "hello" -a myapp # RichEdit/compose box via LegacyIAccessible
如果这三种模式都不能设置该值, set-value 则失败并显示指向最后一种方法的明确错误 send-keys 。
并非每个富编辑器都支持编程集。 LegacyIAccessible 回退仅适用于其辅助功能实现
IAccessible::put_accValue的控件 , 本机 Win32 富编辑控件和 Chromium/Electron/WebView2 组合图面通常起作用。 WinUI 3RichEditBox和 WPFRichTextBox不支持编程值设置 — 设计后,它们将其内容公开为只读UI 自动化(文本模式,无可设置的值模式),因此set-value无法写入它们。 使用(需要解锁的前台桌面)供这些用户使用send-keys。
get-value
从元素读取当前值。 使用智能回退链:TextPattern (RichEditBox, Document) → ValuePattern (TextBox, Slider) → SelectionPattern (ComboBox, RadioButton, TabView) →名称(标签)。
winapp ui get-value doc-texteditor-53ad -a notepad # read full document text
winapp ui get-value SearchBox -a myapp # read TextBox content
winapp ui get-value CmbTheme -a myapp # read ComboBox selected item
winapp ui get-value sld-volume-b2c3 -a myapp # read Slider value
winapp ui get-value lbl-title-a1b2 -a myapp --json # JSON: { "elementId": "...", "text": "..." }
winapp ui get-value SearchBox -a myapp --json
winapp ui wait-for SearchBox -a myapp --value "" --timeout 5000
成功读取空文本字段将 "text": ""返回,而不是其辅助功能标签。 仅空白内容也会保留在 JSON 中。
wait-for --value "" 匹配空字段,无论是新鲜字段还是编辑后清除。 若要改为读取辅助功能标签,请使用 get-property --property Name。
焦点
winapp ui focus txt-textbox-a4b1 -a notepad
根据需要激活所选控件的窗口,然后聚焦控件。
选择器是必需的;使用 -a <app> 或 -w <HWND> 选择目标。
成功表示窗口处于前台状态,并且所选控件在命令返回前确认 HasKeyboardFocus 。 该命令最多允许控件报告焦点的 500 毫秒;如果目标消失或失去前台,而不是试图恢复焦点,它将停止。 主窗口前拥有的对话框是不够的:如果这是目标,请在对话框中选择一个控件。
此命令需要解锁的交互式桌面,并且不会绕过Windows激活限制。 如果失败 foreground_not_target,请在重试前手动激活预期窗口并检查是否有阻止对话框。
对于 focus_not_acquired,请检查当前 UI 并选择一个可聚焦控件。
对于stale_element,使用或search重新发现目标inspect。
在发现和重试命令上保留相同的 --on 目标。
滚动到视图中
将元素滚动到可见区域。
winapp ui scroll-into-view itm-targetitem-c3d4 -a myapp
wait-for
等待元素出现、消失或让值达到目标。
winapp ui wait-for Button -a myapp --timeout 5000 # wait for any button
winapp ui wait-for btn-submit-7a90 -a myapp --timeout 5000 # wait for specific element
winapp ui wait-for CounterDisplay -a myapp --value "5" --timeout 5000 # wait for element value (smart fallback)
winapp ui wait-for lbl-status -a myapp --property Name --value "Done" --timeout 5000 # wait for specific property
winapp ui wait-for btn-submit-a1b2 --gone -a myapp --timeout 2000 # wait for element to disappear
winapp ui wait-for lbl-status -a myapp --value "Done" --contains # substring match instead of exact equality
滚动
滚动容器元素。 使用 - 查找(垂直)或search scroll(水平)标记的[scroll:v]可滚动容器[scroll:h]。
# Find which elements are scrollable and in which direction
winapp ui search scroll -a myapp
# pn-scrollview-bfef Pane "scrollView" [scroll:v] (main content, vertical)
# pn-scrollviewer-bfb1 Pane "scrollViewer" [scroll:h] (horizontal list)
# Scroll the main content down
winapp ui scroll pn-scrollview-bfef --direction down -a myapp
# Jump to top/bottom
winapp ui scroll pn-scrollview-bfef --to bottom -a myapp
# If you target an element that's not scrollable, scroll walks up to find the nearest scrollable parent
winapp ui scroll itm-someitem-a1b2 --direction down -a myapp
# Synthesize real mouse-wheel input over the element (1 = one notch up, -1 = one notch down).
# Use this to test handlers that respond to the wheel directly (zoom, custom scroll) rather than ScrollPattern.
winapp ui scroll img-map-a1b2 --wheel -1 -a myapp
选项:
-
--direction <up|down|left|right>— 通过ScrollPattern. 以增量方式滚动。 -
--to <top|bottom>- 通过ScrollPattern. 跳转到开始/结束。 -
--wheel <notches>- 通过,在SendInput方向盘凹槽(detents):1= 一个凹槽向上/离开,-1= 一个凹槽向下/向下/向上合成元素中心的鼠标滚轮输入,3= 三个凹槽向上。 (每个凹槽是消耗的 120 个单位WHEEL_DELTA的WindowsSendInput;CLI 会为你缩放 120 个单位。ScrollPattern绕过 。
--direction,--to并且--wheel是相互排斥的 - 传递恰好一个。 由于--wheel在屏幕坐标处注入 OS 范围的输入,因此它会先将目标引入前台,如果无法传输焦点,而不是滚动错误的窗口,它将失败(foreground_not_target)。
get-focused
winapp ui get-focused -a myapp
winapp ui get-focused -w <HWND> --json
显示当前在所选应用中具有键盘焦点的元素,包括仅通过其父窗口提供其应用所有权的控件。
使用 -w时,焦点必须属于该窗口,而不是同一进程中的另一个窗口或拥有的弹出窗口。 使用 -a时,将包含所选进程中的其他窗口。
JSON 输出在 hasFocus:false 没有焦点元素可以验证为属于目标时。 如果焦点或窗口所有权查询失败,命令将退出非零;重试 get-focused,并重新发现窗口 list-windows (如果已关闭)。
list-windows
列出应用的所有可见窗口,包括弹出窗口和对话框。 默认情况下,排除无标题窗口(不可见系统窗口)。
winapp ui list-windows -a imageresizer
winapp ui list-windows -a Terminal
winapp ui list-windows # all windows (no filter)
winapp ui list-windows --show-hidden # include invisible zero-size windows
暂停
提前发布此工作流的 UI,而不是等待四秒空闲宽限。 要求 WINAPP_UI_WORKFLOW_ID;不采用任何应用和选择器。 请参阅 提前发布转折。
winapp ui yield
winapp ui yield --json # {"released": true} — or false when nothing was held
框架支持
| Framework | 检查 | 搜索 | 调用 | set-value | 屏幕截图 |
|---|---|---|---|---|---|
| WPF | ✅ 完整树 | ✅ 所有属性 | ✅ 所有模式 | ✅ ¹ | ✅ |
| WinForms | ✅ | ✅ | ✅ | ✅ | ✅ |
| Win32 | ✅ | ✅ | ✅ | ✅ | ✅ |
| WinUI 3 | ✅ | ✅ | ✅ | ✅ ¹ | ✅ |
| 电子 | ⚠️ Chromium 树 | ⚠️ 有限公司 | ⚠️ 变化 | ⚠️ 变化 | ✅ |
| Flutter | ⚠️ 基本 | ⚠️ 基本 | ❌ 最小 | ❌ | ✅ |
¹ set-value 适用于任何公开 ValuePattern/RangeValuePattern 的控件,以及仅 TextPattern 编辑其辅助功能实现 IAccessible::put_accValue 的控件(LegacyIAccessible 回退)。
WinUI 3 RichEditBox 和WPFRichTextBox是例外 — 它们仅公开只读文本模式(无可设置的值模式),因此无法按设计以编程方式设置它们;使用send-keys(需要交互式桌面)键入它们。
使用你自己的代码中的引擎
一切 winapp ui 功能都可用作库,因此,你可以从测试或工具中驱动相同的自动化,而无需启动到 CLI:
| Package | 新增的内容 |
|---|---|
Microsoft.Windows.SDK.BuildTools.WinApp.UIAutomation |
检查、选择器、UIA 模式交互、输入注入、屏幕截图 |
Microsoft.Windows.SDK.BuildTools.WinApp.UIAutomation.Recording |
将视频录制到 MP4,以及帧捆绑包 |
var services = new ServiceCollection().AddLogging().AddWinAppUiAutomation().BuildServiceProvider();
var ui = services.GetRequiredService<IUiAutomation>();
var target = UiTarget.FromWindowHandle(myWindowHandle);
var save = await ui.FindSingleElementAsync(target, new UiSelector { Query = "Save" }, default);
await ui.InvokeAsync(target, save!, default);
对于确定性操作,请使用重载执行 UiInvokeAction:
var selected = await ui.FindSingleElementAsync(
target, new UiSelector { Query = "Save" }, requireUnique: true, default);
if (selected is null) throw new InvalidOperationException("Save was not found.");
UiInvokeActionResult result = await ui.InvokeAsync(target, selected, UiInvokeAction.Invoke, default);
requireUnique: true 拒绝不明确的文本,而不是选择可调用匹配项。 精确 AutomationId 匹配优先于名称或 AutomationId 子字符串;唯一名称仍可以选择其 AutomationId 共享的控件。
对于应用范围内的目标,该检查涵盖其所有应用/拥有的窗口。 使用 -w <HWND> (或显式窗口库目标)限制选择范围。
它返回 Pattern 并 PerformedAction 具有与 CLI 操作结果相同的含义。
传递通过检查或选择返回的元素,其运行时 slug 或 unique AutomationId 保持不变。 显式操作拒绝缺失或歧义标识,而不是按名称和控件类型重新绑定。
对于作用域内读取,请设置为UiSelector.Root另一UiSelectorControlType个类型名称,以及ClassName文本提供程序类。 它们使用与 CLI 相同的 查询谓词 。 仅支持一个根级别: selector.Root.Root 必须是 null。 在查找目标窗口之前, ArgumentException 会引发嵌套根。 使用唯一的根 AutomationId 或 slug,而不是嵌套根选择器。
UiControlTypes.GetId(name) 解析官方类型名称和两个记录的别名,返回 0 无效名称。
UiControlTypes.GetName(id) 返回规范名称或 Unknown(id) 无法识别的 ID。
将UiElement还原的 JSON 传递给或GetTextAsyncGetPropertiesAsync保留其Selector和 WindowHandle。 slug 选择器仍必须标识原始元素;如果它不再存在,这些读取将引发 UiElementNotFoundException ,而不是选择具有相同 AutomationId 或名称的另一个元素。 再次运行原始查询以刷新结果。 范围读取还会从常规 UIA 属性获取器传播失败,并获取 UIA 模式,而不是返回 null 或以前捕获的值。
录制是一个单独的包,以便仅检查和驱动 UI 的项目不会拉取 SkiaSharp。 自动化包同时面向这两个目标net10.0-windowsnet10.0-windows10.0.19041.0;后者添加Windows图形捕获,后者允许screenshot捕获遮挡或 GPU 复合windows。 有关完整 API 和目标框架权衡,请参阅 NuGet 上每个包的自述文件。
UiTarget.FromWindowHandle是已交给窗口的测试框架的入口点,例如MSTest.Windows.UIAutomation,它是WindowTest.MainWindow你与之桥接的 MainWindow.Current.NativeWindowHandleUIA2AutomationElement。
故障排除
| Error | 原因 | 解决方案 |
|---|---|---|
| “找不到正在运行的应用” | 应用未运行或名称不匹配 | 检查进程名称或使用 PID |
| “多个窗口匹配” | 不 -a 明确值 |
从列出的选项中使用-w <HWND> |
| “具有多个窗口” | 进程有多个窗口 | 用于 -w <HWND> 定位特定目标 |
| “选择器匹配 N 个元素” | 不明确的旧选择器 | 将输出中的 slugs inspect 或追加[0][1]到旧版选择器 |
| “元素可能已更改” | Slug 哈希与当前元素不匹配 | 重新运行 inspect 或 search 获取新的污点 |
| “不支持任何调用模式” | 无法调用元素 | 在元素上用于 inspect 查找可调用的子级 |
| “找不到 UIA 窗口” | UIA 看不到该过程 | 用于 list-windows 查找 HWND,然后 -w |
| “窗口大小为零” | 窗口最小化 | 应用将自动还原 |
| 屏幕截图中未显示弹出窗口/下拉列表 | 默认捕获是每窗口,不包括未拥有的覆盖层 | 按照 屏幕截图覆盖工作流 选择一个窗口 -w <hwnd> --capture-screen |
foreground_not_target 来源 --capture-screen |
Windows拒绝激活,因此屏幕捕获会录制任何窗口实际在前面 | 单击目标窗口或关闭焦点窃取窗口,然后重试,或删除 --capture-screen |
element_not_found 记录期间 |
给定选择器但没有匹配元素 | 重新运行 inspect 或 search 获取新的选择器 |
| WGC 在记录期间不可用 | WGC 捕获初始化失败;无静默回退 | 检查 GPU/驱动程序;用于 --capture-screen 同意屏幕 DC 捕获 |
常见模式
导航并验证
winapp ui invoke btn-settings-a1b2 -a myapp # click a button
winapp ui wait-for pn-settingspage-c3d4 -a myapp # wait for page to load
winapp ui screenshot -a myapp --output settings.png # verify visually
查找文本并调用其父级
# Search shows invokable ancestor; invoke auto-walks to it
winapp ui invoke 'Save changes' -a myapp
# Or search first to see what matches, then invoke
winapp ui search "Save changes" -a myapp; winapp ui invoke btn-save-c3d4 -a myapp
消除重复元素的歧义
winapp ui search '#Image' -a myapp; winapp ui invoke itm-image-a2b3 -a myapp
弹出窗口覆盖的屏幕截图
winapp ui list-windows -a myapp # use the main window's HWND below
winapp ui set-value txt-searchbox-e5f6 "query" -a myapp; winapp ui screenshot -w <hwnd> --capture-screen
导航、等待和验证(单链)
winapp ui invoke btn-settings-a1b2 -a myapp; winapp ui wait-for pn-settingspage-c3d4 -a myapp --timeout 3000; winapp ui screenshot -a myapp -o settings.png
发现、单击和验证
winapp ui inspect -a myapp --interactive; winapp ui invoke btn-submit-7a90 -a myapp; winapp ui screenshot -a myapp
文件对话框交互
文件打开/保存对话框是支持 UIA 的标准Windows对话:
# Trigger the dialog, find it, type the path, confirm
winapp ui invoke btn-openfilebtn-a2b3 -a myapp
winapp ui list-windows -a myapp # find dialog HWND
winapp ui set-value txt-1148-c4d5 "C:\path\to\file.png" -w <dialog-hwnd>
winapp ui invoke btn-open-e6f7 -w <dialog-hwnd>
用于 inspect -w <dialog-hwnd> --interactive 发现特定对话的实际污点。
为什么 ; 链接 (不是 &&)
当本机 CLI 写入 stderr 或使用 ANSI 转义序列时,PowerShell 的 && 操作员可能会冻结。 请 ; 改用 — 它无条件地运行每个命令,并避免此死锁。 这也适用于代理工作流:即使调用退出非零,你通常也希望运行屏幕截图。
CI 测试模式
在 CI 管道(GitHub Actions,Azure DevOps)中使用 winapp ui 命令进行冒烟测试和 UI 验证。
wait-for with --property and --value as an assertion — it returns exit code 1 on timeout, failing the CI step automatically.
在 GitHub Actions 中启动和测试
steps:
- name: Build
run: dotnet build MyApp.csproj -c Debug -p:Platform=x64
- name: Launch and test
run: |
$result = winapp run .\bin\x64\Debug\net8.0-windows10.0.26100.0\win-x64 --detach --json | ConvertFrom-Json
$appPid = $result.ProcessId
# Wait for window to initialize
winapp ui wait-for "Main Window" -a $appPid --timeout 30000
# Run tests — each wait-for exits non-zero on failure
winapp ui invoke "Login" -a $appPid
winapp ui wait-for "Dashboard" -a $appPid --timeout 10000
winapp ui screenshot -a $appPid -o dashboard.png
使用 断言元素状态 wait-for
wait-for --value 轮询元素的值与预期的字符串匹配,并使用与 相同的智能回退 get-value (TextPattern → ValuePattern → SelectionPattern → Name)。 返回匹配时退出代码 0,超时退出代码 1 - 使其成为 CI 友好断言。 用于 --property 改为检查特定的 UIA 属性。
# Assert: button click updated the counter (smart value fallback — works for TextBlock, TextBox, etc.)
winapp ui invoke "Counter Button" -a $pid
winapp ui wait-for "Counter Display" -a $pid --value "Count: 1" -t 5000
# Assert: text input was accepted
winapp ui set-value "Search Box" "hello world" -a $pid
winapp ui wait-for "Search Box" -a $pid --value "hello world" -t 3000
# Assert: checkbox was toggled (use --property for specific UIA properties)
winapp ui invoke "Dark Mode" -a $pid
winapp ui wait-for "Dark Mode" -a $pid --property ToggleState --value "On" -t 3000
# Assert: navigation happened (new page appeared)
winapp ui invoke "Settings" -a $pid
winapp ui wait-for "Settings Page" -a $pid -t 10000
# Assert: dialog was dismissed (element disappeared)
winapp ui invoke "Close" -a $pid
winapp ui wait-for "Dialog Title" -a $pid --gone -t 5000
使用 JSON 输出断言
与 PowerShell 或 jq 一 --json 起使用以获取更复杂的断言:
退出代码协定,
searchwait-for并且处于--json模式:当没有元素匹配(search)或等待超时时(wait-for),该命令将完全分析的结果信封写入 stdout({ "matchCount": 0, ... }或{ "found": false, "timedOut": true, ... })并返回退出代码 1。 Stderr 在--json模式下为空(记录器输出已取消)。 信封字段上的分支,或根据$LASTEXITCODE哪个更符合人体工学。
# Assert: search found exactly one match
$result = winapp ui search "Submit" -a $pid --json | ConvertFrom-Json
if ($result.matchCount -ne 1) { throw "Expected 1 Submit button, found $($result.matchCount)" }
# Assert: element has expected properties
# inspect --json returns { windows: [{ hwnd, title, elements: [...] }] };
# each window's elements[] is the nested tree (children rendered via .children).
$tree = winapp ui inspect "Counter Display" -a $pid --json | ConvertFrom-Json
$counter = $tree.windows[0].elements[0]
if ($counter.name -ne "Count: 3") { throw "Counter value wrong: $($counter.name)" }
# Read typed element state while preserving the legacy string property map
$property = winapp ui get-property "Counter Display" -a $pid --json | ConvertFrom-Json
if ($property.element.type -ne "Text") { throw "Unexpected type: $($property.element.type)" }
if ($property.element.isOffscreen) { throw "Counter is offscreen" }
JSON 信封为:
-
inspect:{ "depth", "interactive", "hideDisabled", "hideOffscreen", "windows": [...] } -
search:{ "matchCount", "hasMore", "matches": [...] } -
wait-for:{ "found", "waitedMs", "element"?, "timedOut" } -
get-property:{ "elementId", "element", "properties": { ... } }
类型化元素使用 type 和数值 x、 y数字、 width和 height。
几何图形以物理屏幕像素为单位。
0,0,0,0是UI 自动化此投影中的空/无显示 UI 矩形;isOffscreen是分开的,因此屏幕外元素仍可以具有非零边界。
每个 inspect --jsonwindows[] 条目和 status --json 结果包括 windowDpi、 scale (windowDpi / 96) dpiAwareness和 coordinateSpace: "physical-screen-pixels"。 这些说明目标窗口的 DPI 上下文,而不是无条件监视器 DPI:Windows报告 96 以获取不知道的窗口、系统感知窗口的系统 DPI,以及每个监视器感知窗口的当前监视器 DPI。 如果无法读取 HWND 或 DPI 上下文,命令将失败,而不是以无提示方式替换 96。 在进程具有顶级窗口之前解析进程时 status , hwnd0 将省略 DPI 字段,直到窗口存在。 对于进程范围 inspect,所选目标窗口保持失败速度;如果以后的弹出窗口在读取树后消失,则 windows[] 其条目会携带 dpiError 并省略 DPI 字段,而剩余的窗口树仍返回。
有关每个信封的完整示例,请参阅发货 winapp-ui-automation 技能 references/ui-json-envelope.md 。
完整冒烟测试示例
# Launch
$app = winapp run .\build-output --detach --json | ConvertFrom-Json
# Verify app loaded
winapp ui wait-for "Main Page" -a $app.ProcessId -t 30000
# Interact and assert
winapp ui invoke "Add Item" -a $app.ProcessId
winapp ui set-value "Item Name" "Test Item" -a $app.ProcessId
winapp ui invoke "Save" -a $app.ProcessId
winapp ui wait-for "Test Item" -a $app.ProcessId -t 5000 # assert item appeared in list
winapp ui wait-for "Save" -a $app.ProcessId --gone -t 3000 # assert save dialog closed
# Visual verification
winapp ui screenshot -a $app.ProcessId -o smoke-test.png