The WinAppDriver API, reimplemented on raw IUIAutomation COM, built to
FlaUI's standard of capability.
Point an existing Appium or Selenium suite at it unchanged and stop hitting the ceiling.
Status: incomplete. A rewrite is in progress on
feat/rewrite-jwp-core. Sessions, element find and every element property work. Click, keyboard input, window management, Actions, XPath and screenshots do not yet. See what works before deciding whether it is useful to you.
Three facts define the gap:
- FlaUI reaches UI Automation properly. It is pattern-aware, and it draws an explicit distinction between invoking a UIA pattern and dispatching real mouse input.
- But FlaUI is a .NET library. It cannot be driven from an Appium suite, from Python, or from any existing test that speaks WebDriver. Reaching for it means abandoning the protocol.
- WinAppDriver has the API every existing suite already speaks — and an implementation that is both weak and unmaintained. It is NOT archived — see the note below — but there has been no commit since April 2025, 1155 issues are open, and nothing filed against it will ever be fixed.
So this is not a FlaUI wrapper and not a faithful reimplementation of WinAppDriver's limitations. It serves WinAppDriver's protocol over a UIA layer as capable as FlaUI's.
It depends on Interop.UIAutomationClient — FlaUI's own interop layer, the raw
COM surface, written by FlaUI's author — and deliberately not on
FlaUI.Core. A peer, not a wrapper.
The contract is JSON Wire Protocol, not W3C WebDriver. Where the two disagree, JWP wins; that is what WinAppDriver's own test suite asserts. The previous implementation was built from the W3C spec, and that single wrong choice produced a large share of its failures.
Numbers here were produced by running something. Where conditions were not matched, it says so.
| Result | |
|---|---|
| A property read, before and after handle caching | 19.40 ms → 0.45 ms (43.5x), num5Button, 20 samples |
| An element find, this driver vs WinAppDriver | ~33 ms vs ~1070 ms — unmatched conditions, see caveat below |
| WinAppDriver's score on its own compatibility suite, Windows 11 | 112 / 290 |
The find comparison is a signal, not a benchmark. This driver ran in-process
while WinAppDriver ran over HTTP and re-resolved its element each iteration. It
is worth chasing in bench/, not worth quoting as a headline.
112/290 was an app-drift number and has been retired. Measured 2026-08-10 in
a Windows 10 22H2 VM, WinAppDriver scores 281/290 on its own suite — and of
the 9 failures, 3 are a missing UWP package (0x80073CF1) and 2 need a browser
that is not installed. It passes the suite on the operating system it was built
for. The 178 failures on Windows 11 were Windows 11, not WinAppDriver.
Nothing here is a reason not to build this. The reasons are that WinAppDriver was unmaintained since April 2025 so nothing filed gets fixed, that its click semantics have a documented defect with a reproduction, and that it is roughly 30x slower on a find. None of those needed the score to be bad.
| Area | State |
|---|---|
GET /status, unknown-command fallback |
✅ |
| Session create / list / delete | ✅ |
GET /orientation |
✅ |
Element find — POST /element, /elements |
✅ accessibility id, class name, name, id, tag name |
Element properties — /text, /name, /attribute/{name}, /enabled, /displayed, /selected |
✅ |
Element geometry — /location, /location_in_view, /size |
✅ |
| Stale vs unknown element (status 10 vs 7, including its destructive first-touch behaviour) | ✅ |
| Application launch — classic exe and packaged AUMID | ✅ |
| CLI argument forms and base path | ✅ |
Click, /clear, POST /value, keyboard input |
❌ next |
| Window routes, mouse / touch / Actions | ❌ |
XPath, implicit wait, screenshots, /source |
❌ |
DELETE /session shutting the application down |
❌ leaks a process per session |
docs/LIMITATIONS.md is the live list, including the
deliberate divergences and the things the tests cannot prove.
dotnet run --project src/WindowsDriverCore.HostPort 4723 is shared with WinAppDriver and with Appium. Running two of them means moving one, which people already do by hand. Two environment variables move this one without touching the arguments a suite already passes:
WINDOWSDRIVERCORE_PORT=4899
WINDOWSDRIVERCORE_HOST=0.0.0.0An explicit argument still wins over both. A collision fails in about a second
with Failed to bind to address … address already in use rather than starting
and misbehaving.
WinAppDriver's argument forms are accepted unchanged:
WindowsDriverCore.exe # 127.0.0.1:4723
WindowsDriverCore.exe 4727 # port only
WindowsDriverCore.exe 10.0.0.10 4725 # host and port
WindowsDriverCore.exe 10.0.0.10 4723/wd/hub # base path rides on the port argument
WindowsDriverCore.exe * 4723 # all interfacesBuild and test:
dotnet test WindowsDriverCore.slnxsrc/WindowsDriverCore.Host composition root, CLI, DI
src/WindowsDriverCore.Protocol JWP surface — routes, envelopes, faults. No UIA.
src/WindowsDriverCore.Automation element find and inspection. Typed in, typed out. No HTTP.
src/WindowsDriverCore.Platform Win32, window discovery, process lifetime
Automation and Platform do not reference ASP.NET Core, and that is enforced
by project references rather than by convention. The automation layer is a
usable .NET library on its own — the HTTP server is an adapter over it.
Speed is a design goal, not a later concern. Round trips dominate, because this is cross-process COM, so the work is in making fewer of them: hold the element the caller already named, fetch more per trip, own COM lifetime explicitly rather than leaving it to finalizers.
Compatibility floor is Windows 10 1607 / Server 2016, matching WinAppDriver's, so GitHub's Windows runners are in scope.
A claim written in this repository is not evidence.
Wire behaviour comes from recordings captured against the real WinAppDriver, not
from reading the specification. Five load-bearing claims here turned out to be
wrong, each inherited from an earlier session and repeated without being checked;
docs/PROJECT-KNOWLEDGE.md §0 lists them and what
each one cost.
The most expensive was a rule — never hold an element between calls, it goes stale — which forced a tree walk on every command and was refuted by one experiment. Doctrine is the easiest kind of claim to inherit, because it does not look like a claim.
Two specific things this project does not claim:
- It does not fix WinAppDriver issue #857. Those elements are absent from the
UIA tree entirely; Inspect.exe cannot see them either, so no client can. See
docs/FOUNDING-PREMISE.md. - The capability claim is the one with evidence.
docs/CLICK-SEMANTICS.mdhas a documented cause, a reproduction, and a measured before/after from a real application suite.
| File | What it is |
|---|---|
docs/PROJECT-KNOWLEDGE.md |
The single consolidated briefing — protocol contract, route table, measured ground truth, UIA/COM knowledge, mistakes not to repeat |
docs/LIMITATIONS.md |
Live list of what does not work, and why |
docs/CLICK-SEMANTICS.md |
The pattern ladder, and the field evidence for it |
docs/FOUNDING-PREMISE.md |
The two bug reports this project was founded on, and how both were misdescribed |
docs/REWRITE-SPEC.md |
Architecture and conventions |
MIT. See LICENSE.txt.
Not affiliated with Microsoft. WinAppDriver is Microsoft's, unmaintained since April 2025; FlaUI is Roemer's and is a peer project rather than a dependency.