Component: agents/windows/ — Windows in-VM agent
C# / ASP.NET Core project that runs inside a Windows 11 ARM64 VM. Implements the TestAnyware agent HTTP surface on port 8648 using UI Automation via FlaUI.
Layout
agents/windows/
├── TestAnywareAgent.csproj
├── Program.cs # app bootstrap, endpoint registration
├── AccessibilityEndpoints.cs # /windows, /snapshot, /inspect, /press, ...
├── SystemEndpoints.cs # /health, /exec, /upload, /download, /shutdown
├── Services/ # UIA helpers, window enumerator, tree walker
├── Models/
│ ├── ElementInfo.cs
│ ├── Requests.cs # ElementQuery, SnapshotRequest, ExecRequest, ...
│ ├── Responses.cs
│ ├── UnifiedRole.cs # C# mirror of UnifiedRole
│ └── WindowInfo.cs
├── bin/, obj/ # build outputs (gitignored)
Key design notes
- ASP.NET minimal APIs — one
WebApplication, endpoints wired viaapp.MapGet/app.MapPost. - FlaUI for UIA — abstracts the differences between UIA2, UIA3, and various native COM quirks.
- Self-contained publish. Installed into the Windows golden image
as a single-folder
.NET 9 win-arm64publish; no runtime install required.
Endpoint wiring
From Program.cs / SystemEndpoints.cs / AccessibilityEndpoints.cs:
GET /health
POST /windows, /snapshot, /inspect
POST /press, /set-value, /focus, /show-menu
POST /window-focus, /window-resize, /window-move, /window-close, /window-minimize
POST /wait
POST /exec, /upload, /download, /shutdown
Wire shapes are documented in docs/architecture/agent-protocol.md.
Build / test
Cross-built on the macOS host (no Windows build machine needed):
cd agents/windows
dotnet build -r win-arm64 --no-self-contained # produces ARM64 binaries
dotnet publish -c Release -r win-arm64 --no-self-contained
# Publish output ends up at bin/Release/net9.0/win-arm64/publish/
dotnet test # unit tests (if present)
The --no-self-contained flag keeps the publish small — the Windows
golden image has the .NET 9 runtime installed, so the agent doesn't
need to bundle it.
The publish directory is copied into the autounattend media that
installs the Windows golden image; Task Scheduler registers a logon
task named TestAnywareAgent that starts the binary as the admin
user at desktop login.
Common pitfalls
- UIA ids differ from AX ids.
ElementInfo.idwill never match a macOS AXidfor "the same" element — they come from different subsystems. Use role + label + window for cross-platform selectors. - Virtio networking driver. Without the VirtIO net driver
installed during OOBE (via
autounattend.xml), the agent will be unreachable from the host. This is handled in the golden image build; worth remembering if you customise the autounattend. - First-logon OOBE animation. Disabled in the golden. If you
start a VM with
--viewerand the animation appears, the golden image build skipped a customisation step. - Shell-style exec.
/execruns viacmd.exe /cso shell metacharacters behave as expected; this differs from Linux (sh -c) only in quoting rules.