Screenshot WinUI 3 / DirectComposition windows that GDI captures as blank white.
A tiny (~150-line, single-file) Windows command-line tool that captures one frame of a window or monitor to a PNG, using Windows.Graphics.Capture (WGC).
glasscap 0 desktop.png # whole primary monitor
glasscap 723984 app.png # a single window, by handleIf you've tried to screenshot a WinUI 3 app — or any window whose content is drawn through DirectComposition (UWP, hardware-accelerated WPF, games, video) — the classic capture APIs betray you:
BitBlt/CopyFromScreen→ blank whitePrintWindow(even withPW_RENDERFULLCONTENT) → blank white
That's not a bug in your code. WinUI 3 content is composited by the Desktop Window Manager on the GPU and never touches the GDI device context those old APIs read from. They copy an empty surface. This breaks automated UI screenshots, visual regression tests, and CI screenshot jobs for modern Windows apps.
Windows.Graphics.Capture asks the compositor itself for the real, composited
pixels — the exact frame the user sees, WinUI/DirectComposition and all.
glasscap is the smallest possible wrapper around it: spin up a capture session,
grab the first frame, encode it to PNG, exit. No GUI, no dependencies beyond the
OS, friendly exit codes for scripting.
Grab glasscap-win-x64.exe or glasscap-win-arm64.exe from the
latest release.
These are self-contained single-file executables — drop one on the box and run it.
dotnet publish glasscap.csproj -c Release -r win-x64 --self-contained true \
-p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o distusage: glasscap <hwnd|0> <out.png>
<hwnd> decimal window handle to capture
0 capture the primary monitor (whole composited desktop)
<out.png> output PNG path
On success it prints OK <width>x<height> -> <path> and exits 0. On failure it
writes a diagnostic (with the failing HRESULT) to stderr and returns a non-zero code.
# PowerShell — the main window of a running process
(Get-Process YourApp).MainWindowHandleglasscap (Get-Process YourApp).MainWindowHandle shot.pngIf you just want the whole screen, skip the hunt and pass 0.
The reason this exists: screenshotting a WinUI 3 app from a GitHub Actions runner. GDI-based screenshot steps capture the bare runner desktop and never the app. WGC captures the real thing — even under WARP (the software D3D fallback) when the runner has no GPU.
- name: Launch app and screenshot
shell: pwsh
run: |
$p = Start-Process .\MyWinUIApp.exe -PassThru
Start-Sleep -Seconds 5 # let the window compose its first frame
glasscap $p.MainWindowHandle app.png
- uses: actions/upload-artifact@v4
with:
name: screenshots
path: app.pngA short tour, because the interesting parts are the gotchas:
- D3D11 device —
D3D11CreateDevicewithBGRA_SUPPORT, falling back to the WARP software rasterizer (driver type5) so it works on headless CI runners. - Capture item — WGC's
GraphicsCaptureItemcan't be created from C# directly; you go through theIGraphicsCaptureItemInteropCOM interface (CreateForWindow/CreateForMonitor). - The HSTRING gotcha — the activation-class string passed to
RoGetActivationFactorymust be a real HSTRING built withWindowsCreateString. Letting the marshaller auto-convert astringthrows. - One frame — create a free-threaded
Direct3D11CaptureFramePool, start the session, wait (up to 8 s) for the firstFrameArrived, copy the surface to aSoftwareBitmap, and PNG-encode it viaBitmapEncoder.
The whole thing is one file: Program.cs.
- Windows 10 1903 (build 18362) or newer — WGC's monitor/window capture needs it.
- Captures the first composited frame. A window that hasn't drawn yet captures empty — give it a beat after launch (see the CI example).
- The yellow "now recording" capture border is suppressed by capturing a single frame and exiting; on older builds a brief border may flash.
- One window or the primary monitor per invocation (multi-monitor selection is on the roadmap).
glasscap was extracted from the CI tooling of a real WinUI 3 application, where
every other screenshot approach produced a white rectangle. It's deliberately
single-purpose and dependency-free so you can drop it into any build without
dragging in a capture framework. If it saves you the afternoon it cost to discover
that PrintWindow simply does not work on WinUI 3, it did its job.
Issues and PRs welcome. It's a small tool with a clear scope — bug fixes, the roadmap items in CHANGELOG.md, and docs are all fair game.
MIT © Jesus Triana