Ui-automatisering

Inspecteer en communiceer met het uitvoeren van Windows toepassingen vanaf de opdrachtregel. Wordt gebruikt door AI-agents en ontwikkelaars voor het testen van gebruikersinterfaces, foutopsporing en automatisering.

Overzicht

winapp ui biedt opdrachten voor het controleren en gebruiken van Windows app-API's. Maakt gebruik van Windows UI Automation (UIA). Werkt met elke Windows-app: WPF, WinForms, Win32, Electron en WinUI 3. De meeste opdrachten sturen de app via UIA-patronen (geen invoerinjectie); ui click is de uitzondering en maakt gebruik van echte muissimulatie voor besturingselementen die niet worden ondersteund InvokePattern.

Snel aan de slag

# 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

Apps instellen

Op procesnaam

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

Op venstertitel

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

Per PID

winapp ui inspect -a 12345

Door HWND (stabiel β€” overleeft tab-/titelwijzigingen)

# 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

Gebruiken -a voor detectie, -w voor stabiele targeting. Wanneer -a deze overeenkomen met meerdere vensters, worden deze weergegeven met HWND's die u kunt kiezen.

Selectoren

Doelelementen met behulp van de selector die wordt weergegeven in [brackets] inspect/search-uitvoer. Er zijn drie typen selectors:

Selector Meaning Example
MinimizeButton AutomationId (weergegeven als uniek β€” stabiel, voorkeur) winapp ui invoke MinimizeButton -a myapp
btn-close-d1a0 Semantische slug (weergegeven wanneer geen unieke AutomationId) winapp ui invoke btn-close-d1a0 -a myapp
Submit Zoeken in tekst zonder opmaak op naam/AutomationId (hoofdlettergevoelige subtekenreeks) winapp ui invoke Submit -a myapp

AutomationId-selectors zijn id's voor ontwikkelaarssets (AutomationProperties.AutomationId in XAML). Wanneer een AutomationId uniek is in de hele UI-structuur inspect en search deze rechtstreeks als de selector weergeeft, blijven de indelingswijzigingen, lokalisatie en structuurherstructurering behouden.

Slugselectors (bijvoorbeeld btn-close-d1a0) worden gegenereerd wanneer er geen unieke AutomationId bestaat. Indeling: prefix-name-hash. De hash valideert de elementidentiteit, maar kan verlopen nadat de gebruikersinterface is gewijzigd.

Uitvoerindeling controleren

De inspect opdracht toont de elementstructuur met gekleurde uitvoer (selector in cyaan, naam in groen, metagegevens in grijs):

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

Het eerste woord op elke regel is de selector. Gebruik het met andere ui opdrachten. Wanneer een element een unieke AutomationId heeft, wordt het rechtstreeks gebruikt (bijvoorbeeld TabView, NewTabButton). Wanneer er geen unieke AutomationId bestaat, wordt er een gegenereerde slug gebruikt (bijvoorbeeld tab-newtab-5f5b).

Semantische slugs

Slugs gebruiken de notatie: prefix-normalizedname-hash waarbij:

  • voorvoegsel β€” afkorting van 3 letters (btn, txt, chk, cmb, itm, tab, img, lbl, pn, win, grp, lnk, mnu, enz.)
  • normalizedname : alfanumerieke kleine letters van AutomationId (voorkeur) of Naam, maximaal 15 tekens
  • hash : hexhash van 4 tekens van de RuntimeId van het element (valideert elementidentiteit)

Slugs zijn shell-safe (geen speciale tekens), uniek en kunnen rechtstreeks als argumenten worden gebruikt. De hash biedt verouderingsdetectie. Als het element is vervangen, krijgt u het volgende: 'Element is mogelijk gewijzigd. Voer de inspectie opnieuw uit."

Elementen zonder naam of AutomationId geven alleen voorvoegsel en hash weer (bijvoorbeeld pn-c8a3).

Meerdere overeenkomsten ondubbelzinnig maken

Slugs van inspect/search uitvoer zijn uniek, maar kunnen worden gewijzigd in indelingswijzigingen. Gebruik ze boven namen of tekst zonder opmaak wanneer meerdere overeenkomsten overeenkomen. Wanneer een selector dubbelzinnig is, worden alle overeenkomsten met hun slugs afgedrukt, zodat u de juiste kunt kiezen en opnieuw kunt uitvoeren met die 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

Gebruik tekst zonder opmaak om te zoeken naar elementen. Er is geen speciale syntaxis nodig:

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

Wanneer een tekstzoekopdracht overeenkomt met meerdere elementen (bijvoorbeeld SettingsExpander waarbij groep, knop en tekst allemaal dezelfde naam delen), kiest de CLI automatisch het enige aanroepbare element. Als er meerdere aanroepbaar zijn, worden alle overeenkomsten met slugs weergegeven.

Voor niet-aanroepbare zoekresultaten (bijvoorbeeld een TextBlock binnen een knop), wordt automatisch de dichtstbijzijnde aanroepbare bovenliggende bovenliggende waarde weergegeven, het bovenliggende element waarmee invokeu kunt gebruiken. Dit werkt voor alle zoekkiezers:

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

De surfaced selector kan rechtstreeks worden gebruikt:

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

Opdrachten

status

Maak verbinding met een app en geef verbindingsgegevens weer.

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

Inspecteren

Bekijk de structuur van het ui-element. De uitvoer toont semantische slugs met twee spaties inspringing voor de hiΓ«rarchie:

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

Voorbeelduitvoer (standaard):

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)

Voorbeelduitvoer (--interactive β€” alleen aanroepbare elementen, platte lijst):

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)

Elementen kunnen deze statusmarkeringen weergeven:

  • [on] / [off] / [indeterminate] β€” status van wisselknop/selectievakje
  • [collapsed] / [expanded] β€” de status voor bomen, keuzelijsten met invoervak, menu-items uitvouwen/samenvouwen
  • [scroll:v] / [scroll:h] / [scroll:vh] β€” schuifbare container (verticaal, horizontaal of beide)
  • [offscreen] β€” element is niet zichtbaar op het scherm
  • [disabled] β€” element is niet ingeschakeld
  • value="..." β€” huidige tekstinhoud voor bewerkbare elementen (indien anders dan Naam)

Zoek elementen die overeenkomen met een selector. Uitvoer toont semantische slugs:

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

Voorbeelduitvoer:

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

Slugs die worden weergegeven in de uitvoer (bijvoorbeeld btn-minimize-d1a0) kunnen rechtstreeks worden gebruikt met andere opdrachten:

winapp ui invoke btn-minimize-d1a0 -a notepad

get-property

Eigenschapswaarden van een element lezen. Bevat patroonspecifieke status (ToggleState, Waarde, IsSelected, enzovoort).

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

schermopname

Een venster of element vastleggen als PNG. Wanneer er meerdere vensters bestaan (bijvoorbeeld app en dialoogvenster openen), worden ze samengevoegd in één PNG-bestand met elk venster dat is gestikt.

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)

Wanneer dialoogvensters of pop-ups zijn geopend, worden alle vensters samengevoegd in één PNG,zodat u de volledige UI-status in één afbeelding kunt zien.

Gebruik --capture-screen deze optie wanneer u pop-upmenu's, vervolgkeuzelijsten, flyouts of overlays met knopinfo wilt vastleggen. In --capture-screen de modus (en wanneer u het opnieuw probeert nadat een leeg frame is gedetecteerd) wordt het doelvenster eerst naar de voorgrond gebracht; normale vensteropnamen verplaatsen het venster niet.

aanroepen

Programmatisch een element activeren (klik op de knop, schakeloptie in, keuzelijst met invoervak uitvouwen).

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

Probeert patronen in volgorde: InvokePattern β†’ TogglePattern β†’ SelectionItemPattern β†’ ExpandCollapsePattern.

click

Klik op een element op de schermcoΓΆrdinaten met behulp van muissimulatie. Gebruik dit voor besturingselementen die geen ondersteuning bieden InvokePattern (bijvoorbeeld kolomkoppen, lijstitems).

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

Stel een waarde in voor een bewerkbaar element (tekst voor tekstvak/keuzelijst met invoervak, nummer voor schuifregelaar).

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

get-value

Lees de huidige waarde van een element. Maakt gebruik van een slimme terugvalketen: TextPattern (RichEditBox, Document) β†’ ValuePattern (TextBox, Slider) β†’ SelectionPattern (ComboBox, RadioButton, TabView) β†’ Naam (labels).

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

Verplaats de focus van het toetsenbord naar een element.

winapp ui focus txt-textbox-a4b1 -a notepad

scroll-into-view

Schuif een element naar het zichtbare gebied.

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

wachten op

Wacht tot een element wordt weergegeven, verdwijnt of heeft een waarde een doel bereikt.

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

Schuiven

Schuif door een containerelement. Schuifbare containers zoeken met search scroll : zoek [scroll:v] naar (verticale) of [scroll:h] (horizontale) markeringen.

# 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

get-focused

Het element weergeven dat momenteel de toetsenbordfocus heeft.

winapp ui get-focused -a myapp

list-windows

Alle zichtbare vensters voor een app weergeven, inclusief pop-ups en dialoogvensters.

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

Framework-ondersteuning

Raamwerk Inspecteren search aanroepen set-value schermopname
WPF βœ… Volledige structuur βœ… Alle eigenschappen βœ… Alle patronen βœ… βœ…
WinForms βœ… βœ… βœ… βœ… βœ…
Win32 βœ… βœ… βœ… βœ… βœ…
WinUI 3 βœ… βœ… βœ… βœ… βœ…
Elektron ⚠✍ Chroomboom ⚠️ Beperkt βš Ο‘ varieert βš Ο‘ varieert βœ…
Flutter ⚠️ Basis ⚠️ Basis ❌ Minimale ❌ βœ…

Troubleshooting

Fout Oorzaak Oplossing
"Er is geen actieve app gevonden" App wordt niet uitgevoerd of de naam komt niet overeen Procesnaam controleren of PID gebruiken
"Meerdere vensters komen overeen" Dubbelzinnige -a waarde Gebruiken -w <HWND> vanuit de vermelde opties
"heeft meerdere vensters" Proces heeft meerdere vensters Gebruiken -w <HWND> om een specifiek doel te bereiken
"Selector matched N elements" Dubbelzinnige verouderde selector Slugs uit inspect uitvoer gebruiken of toevoegen [0]aan [1] verouderde selectors
"Element kan zijn gewijzigd" Slug-hash komt niet overeen met het huidige element Opnieuw uitvoeren inspect of search verse slugs krijgen
"biedt geen ondersteuning voor een aanroeppatroon" Het element kan niet worden aangeroepen Op inspect het element gebruiken om een aanroepbaar onderliggend element te zoeken
"Geen UIA-venster gevonden" UIA kan het proces niet zien Gebruik list-windows deze om de HWND te vinden en vervolgens -w
"Venster heeft nulgrootte" Venster is geminimaliseerd De app wordt automatisch hersteld
Pop-up/vervolgkeuzelijst niet in schermopname PrintWindow legt geen overlays vast Vlag gebruiken --capture-screen

Algemene patronen

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

Tekst zoeken en het bovenliggende item aanroepen

# 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

Dubbele elementen ondubbelzinnig maken

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

Schermopname met pop-up-overlays

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

Ontdekken, klikken en controleren

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

Interactie in dialoogvenster Bestand

Dialoogvensters voor openen/opslaan van bestanden zijn standaarddialoogvensters Windows met UIA-ondersteuning:

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

Gebruik inspect -w <dialog-hwnd> --interactive deze functie om de werkelijke slugs voor een specifiek dialoogvenster te ontdekken.

Waarom ; ketenen (niet &&)

De operator van && PowerShell kan blokkeren wanneer een systeemeigen CLI schrijft naar stderr of ANSI-escapereeksen gebruikt. In ; plaats daarvan wordt elke opdracht voorwaardelijke uitgevoerd en wordt deze impasse vermeden. Dit is ook beter voor agentwerkstromen: meestal wilt u dat de schermafbeelding wordt uitgevoerd, zelfs als de aanroep een afsluit zonder nul heeft.

CI-testpatronen

Gebruik winapp ui opdrachten in CI-pijplijnen (GitHub Actions, Azure DevOps) voor betrouwbaarheidstests en ui-validatie. wait-for met --property en --value fungeert als een assertie: het retourneert afsluitcode 1 bij time-out, waarbij de CI-stap automatisch mislukt.

Starten en testen 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

Status van element assertie met wait-for

wait-for --value pollt totdat de waarde van een element overeenkomt met de verwachte tekenreeks, waarbij dezelfde slimme terugval wordt gebruikt als get-value (TextPattern β†’ ValuePattern β†’ SelectionPattern β†’ Name). Retourneert afsluitcode 0 bij overeenkomst, afsluitcode 1 bij time-out, waardoor deze een CI-vriendelijke assertie is. Gebruik --property in plaats daarvan om een specifieke UIA-eigenschap te controleren.

# 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 met JSON-uitvoer

Gebruik --json met PowerShell of jq voor complexere asserties:

Afsluitcodecontract voor search en wait-for in --json de modus: als er geen element overeenkomt (search) of de wachttijd (wait-for), schrijft de opdracht een volledig parseerbare resultaatenvelop naar stdout ({ "matchCount": 0, ... } of { "found": false, "timedOut": true, ... }) en retourneert afsluitcode 1. Stderr is leeg in --json de modus (loggeruitvoer wordt onderdrukt). Vertakking op de envelopvelden, of op $LASTEXITCODE, afhankelijk van welke ergonomischer is.

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

Voorbeeld van een volledige betrouwbaarheidstest

# 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