Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

glasscap

Screenshot WinUI 3 / DirectComposition windows that GDI captures as blank white.

ci license: MIT .NET 8

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 handle

The problem

If 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 white
  • PrintWindow (even with PW_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.

The fix

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.

Install

Download a prebuilt binary (no runtime needed)

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.

Or build from source

dotnet publish glasscap.csproj -c Release -r win-x64 --self-contained true \
  -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o dist

Usage

usage: 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.

Finding a window handle

# PowerShell — the main window of a running process
(Get-Process YourApp).MainWindowHandle
glasscap (Get-Process YourApp).MainWindowHandle shot.png

If you just want the whole screen, skip the hunt and pass 0.

Use it in CI

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.png

How it works

A short tour, because the interesting parts are the gotchas:

  1. D3D11 device — D3D11CreateDevice with BGRA_SUPPORT, falling back to the WARP software rasterizer (driver type 5) so it works on headless CI runners.
  2. Capture item — WGC's GraphicsCaptureItem can't be created from C# directly; you go through the IGraphicsCaptureItemInterop COM interface (CreateForWindow / CreateForMonitor).
  3. The HSTRING gotcha — the activation-class string passed to RoGetActivationFactory must be a real HSTRING built with WindowsCreateString. Letting the marshaller auto-convert a string throws.
  4. One frame — create a free-threaded Direct3D11CaptureFramePool, start the session, wait (up to 8 s) for the first FrameArrived, copy the surface to a SoftwareBitmap, and PNG-encode it via BitmapEncoder.

The whole thing is one file: Program.cs.

Limitations

  • 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).

Why a separate tool?

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.

Contributing

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.

License

MIT © Jesus Triana

About

Screenshot WinUI 3 / DirectComposition windows that GDI captures as blank white — one-frame window/monitor PNG capture via Windows.Graphics.Capture

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages