Automação de UI

Inspecionar e interagir com aplicações Windows em execução a partir da linha de comandos. Usado por agentes e programadores de IA para testes de interface, depuração e automação.

Descrição geral

winapp ui fornece comandos para inspecionar e interagir com Windows interfaces de aplicação. Utiliza Windows Automatização da Interface de Utilizador (UIA). Funciona com qualquer aplicação Windows — WPF, WinForms, Win32, Electron e WinUI 3. A maioria dos comandos conduz a aplicação através de padrões UIA (sem injeção de entrada); ui click é a exceção e usa simulação real de rato para controlos que não suportam InvokePattern.

Início Rápido

# 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

Aplicações de Segmentação

Por nome do 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

Por título da janela

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

Por PID

winapp ui inspect -a 12345

Por HWND (estável — sobrevive a alterações de tab/título)

# 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

Usar -a para descoberta, -w para direcionamento estável. Quando -a corresponde a várias janelas, o comando lista-as com HWNDs para escolheres.

Selecionadores

Elementos-alvo usando o seletor mostrado [brackets] na saída de inspecção/pesquisa. Existem três tipos de selecionadores:

Selector Significado Example
MinimizeButton AutomationId (mostrado quando único — estável, preferencial) winapp ui invoke MinimizeButton -a myapp
btn-close-d1a0 Slug semântico (mostrado quando não existe um Automation Id único) winapp ui invoke btn-close-d1a0 -a myapp
Submit Pesquisa em texto simples contra Name/AutomationId (substring insensível a maiúsculas minúsculas) winapp ui invoke Submit -a myapp

Os seletores AutomationId são identificadores de conjunto de programadores (AutomationProperties.AutomationId em XAML). Quando um AutomationId é único em toda a árvore da interface e inspectsearch o mostra diretamente como seletor — estes sobrevivem a alterações de layout, localização e reestruturação da árvore.

Os seletores de slug (por exemplo, btn-close-d1a0) são gerados quando não existe um AutomationId único. Formato: prefix-name-hash. O hash valida a identidade do elemento, mas pode ficar obsoleto após alterações na interface.

Inspeção do formato de saída

O inspect comando mostra a árvore de elementos com saída colorida (seletor em ciano, nome em verde, metadados em cinzento):

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

A primeira palavra em cada linha é o seletor — usa-o com outros ui comandos. Quando um elemento tem um AutomationID único, é usado diretamente (por exemplo, TabView, NewTabButton). Quando não existe um AutomationId único, é usado um slug gerado (por exemplo, tab-newtab-5f5b).

Slugs semânticos

As lesmas usam o formato: prefix-normalizedname-hash onde:

  • Prefixo — Abreviatura de tipo de 3 letras (BTN, TXT, CHK, CMB, ITM, TAB, IMG, LBL, PN, WIN, GRP, LNK, MNU, ETC.)
  • normalizedname — alfanumérico minúsculo de AutomationId (preferido) ou Name, máximo 15 caracteres
  • hash — hash hexadecimal de 4 caracteres do RuntimeId do elemento (valida a identidade do elemento)

As lesmas são seguras contra conchas (sem caracteres especiais), únicas e podem ser usadas diretamente como argumentos. O hash fornece deteção de estagnação — se o elemento foi substituído, obtém: "O elemento pode ter mudado. Inspecionar novamente."

Elementos sem nome ou AutomationId mostram apenas prefixo + hash (por exemplo, pn-c8a3).

Desambiguação de múltiplos combates

Os slugs da inspect/search saída são únicos, mas podem mudar por alterações de layout – use-os em vez de nomes de tipos simples ou texto quando há múltiplas correspondências. Quando um seletor é ambíguo, o CLI imprime todas as correspondências com os seus slugs para que possas escolher o certo e voltar a executar com esse 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

Use texto simples para procurar elementos — sem necessidade de sintaxe especial:

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 uma pesquisa de texto corresponde a vários elementos (por exemplo, SettingsExpander onde Group, Button e Text partilham o mesmo nome), a CLI escolhe automaticamente o único elemento invocável. Se vários forem invocáveis, lista todas as correspondências com slugs.

Para resultados de pesquisa não invocáveis (por exemplo, um TextBlock dentro de um Botão), a pesquisa revela automaticamente o ancestral invocável mais próximo — o elemento pai que pode usar com invoke. Isto funciona para todos os seletores de pesquisa:

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

O seletor de superfície pode ser usado diretamente:

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

Comandos

estado

Liga-te a uma aplicação e mostra as informações de ligação.

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

inspecionar

Veja a árvore de elementos da interface. A saída mostra slugs semânticos com indentação de 2 espacios para hierarquia:

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

Exemplo de saída (padrão):

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)

Saída de exemplo (--interactive — apenas elementos invocáveis, lista plana):

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)

Os elementos podem mostrar estes marcadores de estado:

  • [on] / [off] / [indeterminate] — estado toggle/checkbox
  • [collapsed] / [expanded] — expandir/colapsar o estado para árvores, caixas de combo, itens do menu
  • [scroll:v] / [scroll:h] / [scroll:vh] — contentor rolável (vertical, horizontal ou ambos)
  • [offscreen] — elemento não é visível no ecrã
  • [disabled] — elemento não está ativado
  • value="..." — conteúdo textual atual para elementos editáveis (quando diferente do Nome)

Encontre elementos que correspondam a um seletor. A saída mostra slugs semânticos:

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

Exemplo de saída:

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

Slugs mostrados na saída (por exemplo, btn-minimize-d1a0) podem ser usados diretamente com outros comandos:

winapp ui invoke btn-minimize-d1a0 -a notepad

get-property

Leia os valores das propriedades a partir de um elemento. Inclui o estado específico do padrão (ToggleState, Value, IsSelected, etc.).

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

captura de ecrã

Captura uma janela ou elemento como PNG. Quando existem várias janelas (por exemplo, app + diálogo aberto), são compostas numa única PNG com cada janela costurada.

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 os diálogos ou pop-ups estão abertos, todas as janelas são compostas numa só PNG para que possas ver o estado completo da interface numa única imagem.

Usa --capture-screen quando precisares de capturar menus pop-up, menus suspensos, flyouts ou sobreposições de tooltip. No --capture-screen modo (e ao tentar novamente após detetar um frame em branco), a janela alvo é trazida primeiro para o primeiro plano; as capturas normais de janelas não movem a janela.

invocar

Ativar programaticamente um elemento (clicar num botão, alternar uma caixa, expandir a caixa de combo).

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

Tenta padrões por ordem: InvokePattern → TogglePattern → SelectionItemPattern → ExpandCollapsePattern.

clicar

Clica num elemento nas coordenadas do ecrã usando simulação de rato. Use isto para controlos que não suportam InvokePattern (por exemplo, cabeçalhos de colunas, itens de lista).

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

valor de conjunto

Defina um valor num elemento editável (texto para TextBox/ComboBox, número para Slider).

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

get-value

Leia o valor atual de um elemento. Utiliza uma cadeia de recurso inteligente: TextPattern (RichEditBox, Document) → ValuePattern (TextBox, Slider) → SelectionPattern (ComboBox, RadioButton, TabView) → Nome (etiquetas).

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

Mude o foco do teclado para um elemento.

winapp ui focus txt-textbox-a4b1 -a notepad

rolar para a vista

Desloca um elemento para a área visível.

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

espera-por

Espere que um elemento apareça, desapareça ou que um valor alcance um alvo.

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

Pergaminho

Desloca um elemento de contentor. Encontre recipientes roláveis com search scroll — procure [scroll:v] marcadores (verticais) ou [scroll:h] (horizontais).

# 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

Foca-te

Mostra o elemento que atualmente tem foco no teclado.

winapp ui get-focused -a myapp

janelas de lista

Liste todas as janelas visíveis de uma aplicação, incluindo pop-ups e diálogos.

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

Suporte ao Quadro

Framework inspecionar pesquisa invocar valor de conjunto captura de ecrã
WPF ✅ Árvore completa ✅ Todas as propriedades ✅ Todos os padrões
WinForms
Win32
WinUI 3
Elétron ⚠️ Árvore de crómio ⚠️ Limitado ⚠️ Varia ⚠️ Varia
Flutter ⚠️ Básico ⚠️ Básico ❌ Minimal

Troubleshooting

Erro Motivo Solution
"Nenhuma aplicação de corrida encontrada" A aplicação não está a correr ou não corresponde com nomes Verifique o nome do processo ou use o PID
"Múltiplas janelas coincidem" Valor ambíguo -a Utilização -w <HWND> a partir das opções listadas
"tem várias janelas" O processo tem múltiplas janelas Usar -w <HWND> para atingir um específico
"Seletor correspondeu a N elementos" Seletor legado ambíguo Use slugs da inspect saída, ou adicione [0], [1] aos seletores legados
"O elemento pode ter mudado" O slug hash não corresponde ao elemento atual Repetir inspect ou search comprar bales frescas
"não suporta qualquer padrão de invocação" Elemento não pode ser invocado Use inspect no elemento para encontrar uma criança invocável
"Nenhuma janela UIA encontrada" A UIA não consegue ver o processo Use list-windows para encontrar o HWND, então -w
"Janela tem tamanho zero" A janela é minimizada A aplicação será restaurada automaticamente
Popup/menu suspenso que não aparece na captura de ecrã O PrintWindow não capta sobreposições Usar --capture-screen flag

Padrões comuns

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

Encontre texto e invoque o seu pai

# 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

Desambiguar elementos duplicados

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

Captura de ecrã com sobreposições pop-up

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

Descobre, clique e verifique

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

Interação de diálogo de ficheiros

Os diálogos de abertura/gravação de ficheiros são diálogos padrão do Windows com suporte a UIA:

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

Use inspect -w <dialog-hwnd> --interactive para identificar as lesmas reais para uma conversa específica.

Porquê ; para encadear (não &&)

O operador do && PowerShell pode congelar quando uma CLI nativa escreve para stderr ou usa sequências de escape ANSI. Use ; em vez disso — executa cada comando incondicionalmente e evita este bloqueio. Isto também é melhor para fluxos de trabalho de agentes: normalmente queres que a captura de ecrã seja executada mesmo que o invoke tenha tido uma saída diferente de zero.

Padrões de Teste de IC

Use comandos winapp ui em pipelines CI (GitHub Actions, Azure DevOps) para testes de fumo e validação da interface. wait-for com --property e --value atua como uma asserção — devolve o código de saída 1 no timeout, falhando automaticamente o passo CI.

Iniciar e testar no 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

Afirmar o estado do elemento com wait-for

wait-for --value sondagens até que o valor de um elemento corresponda à cadeia esperada, usando o mesmo recurso inteligente que get-value (TextPattern → ValuePattern → SelectionPattern → Name). Devolve o código de saída 0 ao corresponder, o código de saída 1 ao timeout — tornando-o uma afirmação amiga do CI. Use --property para verificar uma propriedade específica da UIA em vez disso.

# 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

Assert com saída JSON

Use --json com PowerShell ou jq para asserções mais complexas:

Contrato de código de saída para search e wait-for no --json modo: quando nenhum elemento corresponde a (search) ou a espera expira (wait-for), o comando escreve um envelope de resultados totalmente analisável para stdout ({ "matchCount": 0, ... } ou { "found": false, "timedOut": true, ... }) e retorna o código de saída 1. O Stderr está vazio no --json modo (a saída do logger é suprimida). Desvia nos campos envelope, ou em $LASTEXITCODE, dependendo de qual for mais ergonómico.

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

Exemplo de teste completo de fumo

# 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