Labsco
microsoft logo

winapp-ui-automation

✓ Official1,128

by microsoft · part of microsoft/winappcli

Inspect and interact with running Windows app UIs from the command line using UI Automation (UIA). Use when an AI agent or developer needs to inspect a UI element tree, find controls, take screenshots, click buttons, read or set text, or verify UI state in a running Windows app. Works with any framework WinUI 3, WPF, WinForms, Win32, Electron.

🧰 Not standalone. This skill ships with microsoft/winappcli and only works together with that tool — install the tool first, then add this skill.

This is the playbook your agent receives when the skill activates — you don't need to read it to use the skill, but it's here to audit before installing.

When to use

  • Inspecting a running Windows app's UI from the command line
  • AI agents interacting with Windows applications (clicking buttons, reading text, taking screenshots)
  • Verifying UI state during development or testing
  • Automating UI workflows without Playwright or Selenium
  • Debugging WinUI 3, WPF, WinForms, Win32, or Electron app UIs

Common patterns

Discover and interact

# 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

Find visible text and click it

# 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

Disambiguate duplicate elements

# When text search matches multiple elements, the error shows slugs for each — pick the right one
winapp ui invoke Submit -a myapp
# → Selector matched 3 elements:
#   [0] Button "Submit Order" → btn-submitorder-a1b2
#   [1] Button "Submit" → btn-submit-c3d4
# Use the slug: winapp ui invoke btn-submit-c3d4 -a myapp

Key concepts

  • Selector brackets: inspect and search output shows selectors in [brackets] — use the bracketed value with other ui commands. Selectors are either AutomationId (stable, developer-set) or generated slug (e.g., btn-name-hash).
  • AutomationId selectors: When an element has a unique AutomationId, it becomes the selector directly (e.g., [MinimizeButton]). These survive layout changes and localization — preferred for stable targeting.
  • Slug selectors: When no unique AutomationId exists, a generated slug is used (e.g., [btn-close-a2b3]). Format: prefix-name-hash. May go stale after UI changes.
  • Plain text search: search and invoke accept plain text — search Minimize finds elements with "Minimize" in their Name or AutomationId (substring, case-insensitive). No special syntax needed.
  • --interactive flag: Filters to invokable elements only with auto-depth 8 — the fastest way to see what you can click
  • Invokable ancestor surfacing: When a search result isn't invokable, the nearest invokable parent is shown with its selector
  • ; chaining: Chain commands with ; to run multiple operations in one call, reducing agent round-trips
  • -a vs -w: Use -a to find apps by name/title/PID. Use -w <HWND> for stable window targeting
  • Element markers: [on]/[off] for toggles, [collapsed]/[expanded], [scroll:v]/[scroll:h]/[scroll:vh] for scrollable containers, [offscreen], [disabled], value="..." for editable elements

Tips

  • Use --interactive with inspect as your first command — it shows only what you can click
  • Chain commands with ; to reduce round-trips (see note below on why not &&)
  • Use slugs from output to target specific elements — they're hash-validated and shell-safe
  • Use plain text search to find elements: search Minimize, invoke Submit
  • When multiple elements match text search, the error shows slugs for each — pick the right one
  • Use get-property --property ToggleState to verify checkbox/toggle state after invoke
  • scroll auto-finds the nearest scrollable parent
  • Use --capture-screen to capture popup overlays, dropdown menus, and flyouts (also brings the window to the foreground)
  • Use hover before screenshot --capture-screen to capture tooltips and hover-triggered UI
  • Use --focus to foreground the target window before capture without switching to screen-DC capture (default capture path uses Windows.Graphics.Capture and works while occluded)
  • Use --hide-disabled and --hide-offscreen to reduce noise

Why ; instead of &&

Use ; (not &&) to chain commands. PowerShell's && operator can freeze when a native CLI writes to stderr or uses ANSI escape sequences — this causes a pipeline deadlock. ; runs each command unconditionally and avoids this issue. This is also better for agent workflows: you usually want the screenshot to run even if the invoke had a non-zero exit (to see what went wrong).

File dialog workaround

File open/save dialogs are standard Windows dialogs with UIA support. Interact with them using existing commands:

# 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>

Note: The filename input in standard file dialogs typically has AutomationId 1148. Use inspect -w <dialog-hwnd> --interactive to discover the actual slugs.

JSON output envelopes (v0.3.1+)

The --json envelope for ui inspect, ui get-focused, ui search, and ui wait-for was reshaped in v0.3.1. Pre-0.3.1 parsers will silently break — most fields were renamed, removed, or moved into envelopes. Highlights:

  • ui inspect --json now nests elements under windows[].elements[] (was a flat elements[]).
  • ui get-focused --json always emits an envelope — { "hasFocus": false } or { "hasFocus": true, "element": {...} } (was bare null).
  • ui search --json / ui wait-for --json may include an invokableAncestor field (element-shaped) on each match.
  • Per-element id, parentSelector, and windowHandle are removed — use selector as the public handle.

Full schemas with examples: references/ui-json-envelope.md.

  • winapp-setup for adding Windows SDK to your project
  • winapp-package for packaging apps as MSIX

Command Reference

winapp ui status

Connect to a target app and display connection info.

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--jsonFormat output as JSON(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui inspect

View the UI element tree with semantic slugs, element types, names, and bounds.

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--ancestorsWalk up the tree from the specified element to the root(none)
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--depthTree inspection depth4
--hide-disabledHide disabled elements from output(none)
--hide-offscreenHide offscreen elements from output(none)
--interactiveShow only interactive/invokable elements (buttons, links, inputs, list items). Increases default depth to 8.(none)
--jsonFormat output as JSON(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

Search the element tree for elements matching a text query. Returns all matches with semantic slugs.

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--jsonFormat output as JSON(none)
--maxMaximum search results50
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui get-property

Read UIA property values from an element. Specify --property for a single property or omit for all.

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--jsonFormat output as JSON(none)
--propertyProperty name to read or filter on(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui get-value

Read the current value from an element. Tries TextPattern (RichEditBox, Document), ValuePattern (TextBox, ComboBox, Slider), then Name (labels). Usage: winapp ui get-value -a

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--jsonFormat output as JSON(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui screenshot

Capture the target window or element as a PNG image. When multiple windows exist (e.g., dialogs), captures each to a separate file. With --json, returns file path and dimensions. Use --capture-screen for popup overlays.

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--capture-screenCapture from screen DC via BitBlt (includes popups/overlays not owned by the target). Implies --focus.(none)
--focusBring the target window to the foreground before capture. Already implied by --capture-screen.(none)
--jsonFormat output as JSON(none)
--outputSave output to file path (e.g., screenshot)(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui invoke

Activate an element by slug or text search. Tries InvokePattern, TogglePattern, SelectionItemPattern, and ExpandCollapsePattern in order.

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--jsonFormat output as JSON(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui click

Click an element by slug or text search using mouse simulation. Works on elements that don't support InvokePattern (e.g., column headers, list items). Use --double for double-click, --right for right-click.

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--doublePerform a double-click instead of a single click(none)
--jsonFormat output as JSON(none)
--rightPerform a right-click instead of a left click(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui drag

Press the mouse button at one point, move to another, then release. 'drag ', where / are each an element selector (uses the element's center) or app-relative x,y coordinates as reported by 'ui inspect'. Useful for reorder/resize/slider gestures and drag-and-drop. Use --right for a right-button drag, --hold-ms for press-and-hold/long-press, and --dwell-ms to settle on a drop target before releasing.

Arguments

ArgumentRequiredDescription
<from>NoStart point — an element selector (drags from its center) or app coordinates x,y as reported by 'ui inspect' (e.g. pn-list-d736 or 100,200).
<to>NoEnd point — an element selector (drops at its center) or app coordinates x,y as reported by 'ui inspect' (e.g. pn-target-d746 or 300,400).

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--dwell-msMilliseconds to dwell at the destination after moving, before releasing (default: 0). Lets drop targets / merge overlays that arm from a sustained hover latch before release.(none)
--hold-msMilliseconds to hold the button down at the start before moving (default: 0). With == (no movement) this performs a press-and-hold / long-press gesture.(none)
--jsonFormat output as JSON(none)
--rightDrag with the right mouse button instead of the left button(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui hover

Move the mouse to an element's center to trigger hover effects (tooltips, flyouts, visual states). Uses SendInput for realistic mouse movement and waits for a configurable dwell time.

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--dwell-timeTime in milliseconds to wait after hovering for hover effects to appear (default: 800)800
--jsonFormat output as JSON(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui send-keys

Send synthetic keyboard input to a window. Supports named keys (down, enter, tab), modifier combos (ctrl+shift+t), raw virtual keys (vk=0xNN), and literal text. Use --verbatim to type the whole argument literally, or --target to focus an element first. Two transports via --via: post-message (default, HWND-targeted, bypasses UIPI) or send-input (OS-wide). For per-keystroke KeyDown on typed text (e.g. a WinUI 3/WPF TextBox), use --via send-input.

Arguments

ArgumentRequiredDescription
<keys>NoKeys to send. Whitespace-separated tokens: named keys (down, enter, tab, esc, f5), modifier combos (ctrl+shift+t, alt+f4), raw virtual keys (vk=0x42), or literal text (hello). Use text= to type a single value verbatim when it would otherwise be read as a key name or combo (text=enter types "enter"; text=ctrl+a types "ctrl+a"); backslash escapes \s \t \n \r \ are supported (text=a\s\sb types "a b"). To type the whole argument literally without escaping each token, pass --verbatim instead. Quote multi-token strings, e.g. "ctrl+a delete".

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--jsonFormat output as JSON(none)
--targetOptional selector (slug or text) to focus before sending keys.(none)
--verbatimType the entire keys argument as literal text — no named-key, combo, or vk= interpretation, and exact whitespace preserved. The whole-argument form of the per-token text= escape: --verbatim "down down enter" types the words instead of pressing Down, Down, Enter.(none)
--viaTransport: post-message (default, HWND-targeted, bypasses UIPI; typed text raises TextChanged but not a per-character KeyDown) or send-input (OS-wide; typed text raises a real per-character KeyDown + TextChanged). Named keys and combos raise KeyDown on both, but keyboard accelerators/shortcuts (KeyboardAccelerator, e.g. ctrl+t) only fire via send-input.post-message
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui set-value

Set a value on an element using UIA ValuePattern. Works for TextBox, ComboBox, Slider, and other editable controls. Usage: winapp ui set-value -a

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId
<value>NoValue to set (text for TextBox/ComboBox, number for Slider)

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--jsonFormat output as JSON(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui focus

Move keyboard focus to the specified element using UIA SetFocus.

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--jsonFormat output as JSON(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui scroll-into-view

Scroll the specified element into the visible area using UIA ScrollItemPattern.

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--jsonFormat output as JSON(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui scroll

Scroll a container element using ScrollPattern. Use --direction to scroll incrementally, --to to jump to top/bottom, or --wheel to synthesize mouse-wheel input.

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--directionScroll direction: up, down, left, right(none)
--jsonFormat output as JSON(none)
--toScroll to position: top, bottom(none)
--wheelRotate the mouse wheel over the element by this many notches (1 = one notch up, -1 = one notch down). Synthesizes real wheel input instead of using ScrollPattern.(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui wait-for

Wait for an element to appear, disappear, or have a property reach a target value. Polls at 100ms intervals until condition met or timeout.

Arguments

ArgumentRequiredDescription
<selector>NoSemantic slug (e.g., btn-minimize-d1a0) or text to search by name/automationId

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--containsUse substring matching for --value instead of exact match(none)
--goneWait for element to disappear instead of appear(none)
--jsonFormat output as JSON(none)
--propertyProperty name to read or filter on(none)
--timeoutTimeout in milliseconds5000
--valueWait for element value to equal this string. Uses smart fallback (TextPattern -> ValuePattern -> Name). Combine with --property to check a specific property instead.(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)

winapp ui list-windows

List all visible windows with their HWND, title, process, and size. Use -a to filter by app name. Use the HWND with -w to target a specific window.

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--jsonFormat output as JSON(none)
--show-hiddenInclude untitled zero-size windows that are hidden by default(none)

winapp ui get-focused

Show the element that currently has keyboard focus in the target app.

Options

OptionDescriptionDefault
--appTarget app (process name, window title, or PID). Lists windows if ambiguous.(none)
--jsonFormat output as JSON(none)
--windowTarget window by HWND (stable handle from list output). Takes precedence over --app.(none)