Automazione dell'interfaccia utente

Esaminare e interagire con l'esecuzione di applicazioni Windows dalla riga di comando. Usato dagli agenti di intelligenza artificiale e dagli sviluppatori per i test, il debug e l'automazione dell'interfaccia utente.

Overview

winapp ui fornisce comandi per l'ispezione e l'interazione con le interfacce utente dell'app Windows. Usa Windows Automazione interfaccia utente (UIA). Funziona con qualsiasi app Windows: macchine virtuali Windows, WinForms, Win32, Electron e WinUI 3. La maggior parte dei comandi guida l'app tramite modelli di interfaccia utente (senza inserimento di input); ui click è l'eccezione e usa una simulazione reale del mouse per i controlli che non supportano InvokePattern.

Avvio rapido

# 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

Selezione di app di destinazione

In base al nome del processo

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

In base al titolo della finestra

winapp ui inspect -a "LICENSE - Notepad"
winapp ui inspect -a "Fix WinApp"     # partial title match

Da PID

winapp ui inspect -a 12345

Da HWND (stabile — sopravvive alle modifiche di tabulazioni/titolo)

# 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

Usare -a per l'individuazione, -w per la destinazione stabile. Quando -a corrisponde a più finestre, il comando li elenca con HWND da selezionare.

Selettori

Elementi di destinazione che usano il selettore visualizzato in nell'output [brackets] di ispezione/ricerca. Esistono tre tipi di selettori:

Selettore Meaning Example
MinimizeButton AutomationId (visualizzato quando univoco, stabile, preferito) winapp ui invoke MinimizeButton -a myapp
btn-close-d1a0 Slug semantico (visualizzato quando non è univoco AutomationId) winapp ui invoke btn-close-d1a0 -a myapp
Submit Ricerca in testo normale in Nome/AutomationId (sottostringa senza distinzione tra maiuscole e minuscole) winapp ui invoke Submit -a myapp

I selettori AutomationId sono identificatori del set di sviluppatori (AutomationProperties.AutomationId in XAML). Quando un AutomationId è univoco nell'intero albero inspect dell'interfaccia utente e search lo mostra direttamente come selettore, che sopravvive alle modifiche di layout, alla localizzazione e alla ristrutturazione dell'albero.

I selettori slug (ad esempio, btn-close-d1a0) vengono generati quando non esiste un AutomationId univoco. Formato: prefix-name-hash. L'hash convalida l'identità dell'elemento, ma potrebbe non essere aggiornata dopo le modifiche dell'interfaccia utente.

Esaminare il formato di output

Il inspect comando mostra l'albero degli elementi con output colorato (selettore in ciano, nome in verde, metadati in grigio):

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

La prima parola in ogni riga è il selettore, usarla con altri ui comandi. Quando un elemento ha un automationId univoco, viene usato direttamente (ad esempio, TabView, NewTabButton). Quando non esiste alcun AutomationId univoco, viene usato un slug generato ,ad esempio tab-newtab-5f5b.

Slug semantici

I slugs usano il formato: dove: prefix-normalizedname-hash

  • prefisso — abbreviazione di tipo 3 lettere (btn, txt, chk, cmb, itm, tab, img, lbl, pn, win, grp, lnk, mnu e così via)
  • normalizedname : alfanumerico minuscolo da AutomationId (preferito) o Nome, massimo 15 caratteri
  • hash : hash esadecimale a 4 caratteri del RuntimeId dell'elemento (convalida l'identità dell'elemento)

I slug sono indipendenti dalla shell (senza caratteri speciali), univoci e possono essere usati direttamente come argomenti. L'hash fornisce il rilevamento di decadimento: se l'elemento è stato sostituito, si ottiene: "L'elemento potrebbe essere stato modificato. Eseguire di rieseguare l'ispezione."

Gli elementi senza nome o AutomationId mostrano solo il prefisso + hash (ad esempio, pn-c8a3).

Disambiguare più corrispondenze

I slugs dall'output inspect/search sono univoci, ma possono cambiare le modifiche di layout: usarle su nomi di tipo normale o testo quando più corrispondenze. Quando un selettore è ambiguo, l'interfaccia della riga di comando stampa tutte le corrispondenze con i relativi slug, in modo da poter scegliere quella corretta e rieseguerla con tale 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

Usare il testo normale per cercare elementi: non è necessaria una sintassi speciale:

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

Quando una ricerca di testo corrisponde a più elementi (ad esempio, SettingsExpander dove Group, Button e Text condividono lo stesso nome), l'interfaccia della riga di comando seleziona automaticamente l'unico elemento richiamabile. Se più sono richiamabili, elenca tutte le corrispondenze con i slug.

Per i risultati di ricerca non richiamabili (ad esempio, textBlock all'interno di un pulsante), la ricerca espone automaticamente il predecessore richiamabile più vicino, ovvero l'elemento padre che è possibile usare con invoke. Questa operazione funziona per tutti i selettori di ricerca:

  lbl-savechanges-a1b2 "Save changes" (120,40 80x20)
        ^ invoke via: btn-save-c3d4 "Save"

Il selettore di superficie può essere usato direttamente:

winapp ui invoke btn-save-c3d4 -a myapp    # invoke the parent Button

Comandi

stato

Connettersi a un'app e visualizzare le informazioni di connessione.

winapp ui status -a notepad
winapp ui status -a notepad --json

Ispezionare

Visualizzare l'albero degli elementi dell'interfaccia utente. L'output mostra i slug semantici con rientro a 2 spazi per la gerarchia:

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

Output di esempio (impostazione predefinita):

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)

Output di esempio ( —--interactive solo elementi richiamabili, elenco flat):

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)

Gli elementi possono mostrare questi marcatori di stato:

  • [on] / [off] / [indeterminate] — Stato attiva/disattiva/casella di controllo
  • [collapsed] / [expanded] — espandere/comprimere lo stato per alberi, caselle combinate, voci di menu
  • [scroll:v] / [scroll:h] / [scroll:vh] — contenitore scorrevole (verticale, orizzontale o entrambi)
  • [offscreen] — l'elemento non è visibile sullo schermo
  • [disabled] — l'elemento non è abilitato
  • value="..." — contenuto di testo corrente per gli elementi modificabili (se diverso da Name)

Trovare elementi corrispondenti a un selettore. L'output mostra i slug semantici:

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

Output di esempio:

  btn-minimize-d1a0 "Minimize" (1222,206 48x48)
  btn-maximize-e2b1 "Maximize" (1270,206 48x48)
  btn-close-d1a2 "Close" (1318,206 48x48)

I slug visualizzati nell'output (ad esempio , btn-minimize-d1a0) possono essere usati direttamente con altri comandi:

winapp ui invoke btn-minimize-d1a0 -a notepad

get-property

Legge i valori delle proprietà da un elemento. Include lo stato specifico del criterio (ToggleState, Value, IsSelected e così via).

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

schermata

Acquisire una finestra o un elemento come PNG. Quando esistono più finestre (ad esempio, finestra di dialogo app + aperta), vengono composte in un singolo PNG con ogni finestra in cui è stato creato un punto.

winapp ui screenshot -a notepad                     # saves screenshot.png in cwd
winapp ui screenshot -a notepad --output my.png     # custom filename
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 -a myapp --capture-screen      # capture from screen (includes popups/overlays)

Quando le finestre di dialogo o i popup sono aperte, tutte le finestre vengono composte in un file PNG, in modo da visualizzare lo stato completo dell'interfaccia utente in un'unica immagine.

Usare --capture-screen quando è necessario acquisire menu popup, elenchi a discesa, riquadri a comparsa o sovrimpressioni delle descrizioni comandi. In --capture-screen modalità (e quando si ritenta dopo il rilevamento di un frame vuoto) la finestra di destinazione viene portata prima in primo piano; le acquisizioni di finestre normali non spostano la finestra.

evocare

Attivare a livello di codice un elemento (fare clic sul pulsante, attivare o disattivare la casella di controllo, espandere la casella combinata).

winapp ui invoke btn-submit-7a90 -a myapp             # by slug from inspect
winapp ui invoke btn-submit-a1b2 -a myapp  # by slug from inspect/search
winapp ui invoke cmb-sizecombobox-b4c5 -a myapp # expand combo box

Prova i criteri in ordine: InvokePattern → TogglePattern → SelectionItemPattern → ExpandCollapsePattern.

click

Fare clic su un elemento in corrispondenza delle coordinate dello schermo usando la simulazione del mouse. Usare questa opzione per i controlli che non supportano InvokePattern (ad esempio, intestazioni di colonna, voci di elenco).

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

set-value

Impostare un valore su un elemento modificabile (testo per TextBox/ComboBox, numero per Slider).

winapp ui set-value txt-textbox-a4b1 "Hello world" -a notepad
winapp ui set-value sld-volume-b2c3 75 -a myapp

get-value

Leggere il valore corrente da un elemento . Usa una catena di fallback intelligente: TextPattern (RichEditBox, Document) → ValuePattern (TextBox, Slider) → SelectionPattern (ComboBox, RadioButton, TabView) → Name (etichette).

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": "..." }

focus

Spostare lo stato attivo della tastiera su un elemento.

winapp ui focus txt-textbox-a4b1 -a notepad

scorrimento nella visualizzazione

Scorrere un elemento nell'area visibile.

winapp ui scroll-into-view itm-targetitem-c3d4 -a myapp

wait-for

Attendere che un elemento venga visualizzato, scomparire o avere un valore raggiunto una destinazione.

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

scorrere

Scorrere un elemento contenitore. Trovare contenitori scorrevoli con search scroll : cercare [scroll:v] indicatori (verticali) o [scroll:h] (orizzontali).

# 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

ottenere l'attenzione

Mostra l'elemento con lo stato attivo della tastiera.

winapp ui get-focused -a myapp

list-windows

Elencare tutte le finestre visibili per un'app, inclusi popup e finestre di dialogo.

winapp ui list-windows -a imageresizer
winapp ui list-windows -a Terminal
winapp ui list-windows                                      # all windows (no filter)

Supporto del framework

Struttura Ispezionare search evocare set-value schermata
macchine virtuali Windows ✅ Albero completo ✅ Tutte le proprietà ✅ Tutti i modelli
WinForms
Win32
WinUI 3
Elettrone ⚠️ Albero chromium ⚠️ Limitato ⚠️ Varia ⚠️ Varia
Flutter ⚠️ Basic ⚠️ Basic ❌ Minimo

Risoluzione dei problemi

Error Motivo Soluzione
"Nessuna app in esecuzione trovata" App non in esecuzione o mancata corrispondenza del nome Controllare il nome del processo o usare PID
"Più finestre corrispondono" Valore ambiguo -a Usare -w <HWND> dalle opzioni elencate
"ha più finestre" Il processo ha più finestre Usare -w <HWND> per specificare come destinazione uno specifico
"Selector matched N elements" Selettore legacy ambiguo Usare i slug dall'output inspect o aggiungere [0], [1] ai selettori legacy
"L'elemento potrebbe essere stato modificato" L'hash slug non corrisponde all'elemento corrente inspect Eseguire nuovamente o search per ottenere nuovi slug
"non supporta alcun modello invoke" Non è possibile richiamare l'elemento Usare inspect sull'elemento per trovare un elemento figlio richiamabile
"Nessuna finestra dell'interfaccia utente trovata" L'interfaccia utente non può visualizzare il processo Usare list-windows per trovare HWND, quindi -w
"La finestra ha dimensioni zero" Finestra ridotta a icona L'app verrà ripristinata automaticamente
Popup/elenco a discesa non incluso nello screenshot PrintWindow non acquisisce sovrapposizioni Usare --capture-screen il flag

Modelli comuni

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

Trovare il testo e richiamarne l'elemento padre

# 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

Disambiguare elementi duplicati

winapp ui search '#Image' -a myapp; winapp ui invoke itm-image-a2b3 -a myapp

Screenshot con sovrimpressioni popup

winapp ui set-value txt-searchbox-e5f6 "query" -a myapp; winapp ui screenshot -a myapp --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

Individuare, fare clic e verificare

winapp ui inspect -a myapp --interactive; winapp ui invoke btn-submit-7a90 -a myapp; winapp ui screenshot -a myapp

Interazione del dialogo file

Le finestre di dialogo di apertura/salvataggio dei file sono finestre di dialogo standard Windows con supporto dell'interfaccia utente:

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

Usare inspect -w <dialog-hwnd> --interactive per individuare i slug effettivi per un dialogo specifico.

Perché ; per il concatenamento (non &&)

L'operatore di PowerShell può bloccarsi quando un'interfaccia della && riga di comando nativa scrive in stderr o usa sequenze di escape ANSI. Usare ; invece : esegue ogni comando in modo incondizionato ed evita questo deadlock. Questo è anche meglio per i flussi di lavoro dell'agente: in genere si vuole che lo screenshot venga eseguito anche se l'invoke ha una uscita diversa da zero.

Modelli di test CI

Usare i comandi winapp ui nelle pipeline CI (GitHub Actions, Azure DevOps) per i test di fumo e la convalida dell'interfaccia utente. wait-for con --property e --value funge da asserzione: restituisce il codice di uscita 1 al timeout, con esito negativo del passaggio CI automaticamente.

Avviare e testare in 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

Stato dell'elemento Assert con wait-for

wait-for --value esegue il polling fino a quando il valore di un elemento corrisponde alla stringa prevista, usando lo stesso fallback intelligente di get-value (TextPattern → ValuePattern → SelectionPattern → Name). Restituisce il codice di uscita 0 alla corrispondenza, il codice di uscita 1 al timeout, rendendolo un'asserzione compatibile con l'integrazione continua. Usare --property invece per controllare una proprietà UIA specifica.

# 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

Asserzione con output JSON

Usare --json con PowerShell o jq per asserzioni più complesse:

Contratto di codice di uscita per search e wait-for in --json modalità: quando nessun elemento corrisponde (search) o il timeout di attesa (wait-for), il comando scrive una busta dei risultati completamente analizzabile in stdout ({ "matchCount": 0, ... } o { "found": false, "timedOut": true, ... }) e restituisce il codice di uscita 1. Stderr è vuoto in --json modalità (l'output del logger viene eliminato). Diramare i campi della busta, o su $LASTEXITCODE, a seconda del quale è più ergonomico.

# 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)" }

Esempio di test di fumo completo

# 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