The in-app half
Everything else here runs against an application that references nothing. Winwright.InApp is
the exception: it is the only thing this project asks you to put inside an application your
users run, which is a different decision from taking a test dependency and deserves a different
answer.
What it does in a release nobody is testing
Section titled “What it does in a release nobody is testing”Nothing.
It runs no thread, watches no directory and holds no timer. Every surface below is armed by an environment variable, and the process that starts your application is the only thing that can set one — so a copy on somebody’s machine, started from their Start menu, answers nothing and writes no file. The work happens on the message loop your application already runs, only when a harness sends the message, and only where the variable named somewhere to write.
That is the property that makes the protocol safe to leave in a shipped build, and it is worth more than any feature list.
| Surface | What it answers | Armed by |
|---|---|---|
Geometry | The geometry of what was drawn, dumped for a harness that has no tree to read. | WINWRIGHT_GEOMETRYThe variable naming the file to dump into. Unset means dump nowhere, for the reason PathVariable means report nothing: an application shipped to its users is not under test, and one writing files because it once was is worse than one that never dumped at all. |
Renders | Rendering this application's own tree when a harness asks for it. | WINWRIGHT_RENDERSThe variable naming the directory renders may be written into. Unset means answer nothing, for the reason PathVariable means report nothing. |
Surfaces | What the application drew, said out loud so a capture can be asserted against it. | WINWRIGHT_SURFACESThe variable naming the file to report into. Unset means report nowhere: an application shipped to its users is not under test, and one writing files because it once was is worse than one that never reported at all. |
What it buys
Section titled “What it buys”2 of the engine’s 121 verbs need it. That is the shorter list than most people guess, and each of them prevents a specific way a harness gets the wrong picture:
- A render of your own visual tree, rather than a copy of the screen. A copy carries whatever was standing in front of the window when it was taken — a notification, somebody else’s dialog, a screen saver — and a render cannot, because there is nothing in front of a visual tree.
- A popup’s own tree, which is the one surface no copy of the screen can take. A popup is layered so it can draw a shadow, so the soft edge of a copy of it is the desktop behind it rather than the popup.
- The rectangle your application actually painted, in physical pixels. A harness in another process can only guess at one, and a guess about a popup, a flyout or a page that scrolled is a capture asserted against a rectangle nobody drew. Layout happens in device-independent units and a copy works in pixels: a rectangle handed over in the wrong one is right at one hundred percent and wrong at every scaling a developer actually runs.
The two halves reference nothing of each other. They find each other by a registered window message and agree on nothing else:
Winwright.OwnRenderWinwright.OwnRender.PopupWinwright.OwnRender.Why
Each ask is its own message rather than a field added to the one before it, so an application shipping an older half than the harness driving it simply does not answer the newer ask — rather than reading a two-field payload as a path. Which way that skew runs is not hypothetical: this half is what you ship, and it reaches your users by a release.
The failure you meet without it
Section titled “The failure you meet without it”A run that asks for a render from an application with no in-app half does not fail, and does not quietly take a screen copy instead. It answers a hole, naming the condition:
an application that renders its own tree when asked
That is the desk-free half of reading the verdict: the assertion
never ran, it is named in the summary, and the exit code is 2. If that is the message that
brought you here, this page is the answer to it.
Taking it
Section titled “Taking it”It needs <UseWPF>true</UseWPF> in the application's project.
<PackageReference Include="Winwright.InApp" Version="1.0.0" />Then arm it once, wherever your application starts, and dispose what comes back when it stops. Everything after that is the harness’s business.
Where to go next
Section titled “Where to go next”- The verbs — the full list, with what each one needs.
- Reading the verdict — what a hole is, and every condition that produces one.
- The README — the arming call, and what each surface reports.