# How Apps Protect Windows from Screenshots: Windows, macOS, and Linux in Detail
When you try to take a screenshot of Netflix or Spotify, instead of the video you'll see a black rectangle—that's not a bug, but deliberate protection via system APIs. Developers use flags like SetWindowDisplayAffinity on Windows or sharingType on macOS to exclude the window from screen capture streams. But in macOS 15 Sequoia, Apple broke compatibility, and on Linux everything depends on the display server. Let's break down how protection works on each platform, where it fails, and what workarounds exist.
Screen Capture Mechanisms in Windows: Three Main APIs
When an app like Zoom requests screen sharing access, it subscribes to a frame stream from the operating system. Windows has three main ways to get this stream, each with its own quirks:
- GDI BitBlt from desktop DC—the oldest method, working since Windows 2000. It uses the
BitBltcall fromGetDC(NULL)to copy pixels from the DWM (Desktop Window Manager) surface into any HDC. Slow, no hardware acceleration, but universal. Used in legacy apps and monitoring tools. - Desktop Duplication API (DXGI)—introduced in Windows 8. Via
IDXGIOutputDuplication::AcquireNextFrame, it returns a GPU texture with the fully composited desktop frame. Fast, hardware-accelerated, but captures only the entire monitor. Used in classic versions of Zoom, TeamViewer, AnyDesk, and Teams. - Windows.Graphics.Capture (WGC)—the modern standard, introduced in Windows 10 1803. Supports capturing either the entire monitor or a specific window by HWND. Used in current versions of OBS, Microsoft Teams, Chromium, Zoom, and the built-in Snipping Tool.
All three methods ultimately read data from DWM—the single place where the final screen image is composed. This is where protection mechanisms come into play.
SetWindowDisplayAffinity: Modes and Their Behavior
DWM lets each window set a display affinity attribute via the SetWindowDisplayAffinity function. This attribute determines whether the window appears in capture streams. Three values are available:
WDA_NONE(0x00)—window is visible in all captures (default).WDA_MONITOR(0x01)—window displays on the monitor but is replaced by a black rectangle in captures. Works since Windows Vista. Netflix uses this for content protection.WDA_EXCLUDEFROMCAPTURE(0x11)—window is completely excluded from the capture stream, as if it doesn't exist. Introduced in Windows 10 May 2020 Update (build 19041). Content from underlying windows shows through—cleaner solution.
C++ example:
#include <windows.h>
#include <cstdio>
int main() {
HWND hwnd = GetConsoleWindow();
if (!SetWindowDisplayAffinity(hwnd, WDA_EXCLUDEFROMCAPTURE)) {
printf("SetWindowDisplayAffinity failed: %lu\n", GetLastError());
return 1;
}
printf("Okno teper nevidimo for zakhvata. Poprobuy sdelat skrinshot.\n");
Sleep(60000);
return 0;
}
It works similarly in C# via P/Invoke, in Electron via setContentProtection(true), and in Tauri via with_content_protection(true).
Under the hood, DWM separates compositing pipelines: a window with WDA_EXCLUDEFROMCAPTURE only goes into the pipeline for the physical monitor, not for capture clients. You can't bypass this at the pixel-reading level.
Compatibility and Limitations in Windows
Testing on Windows 11 23H2 showed that WDA_EXCLUDEFROMCAPTURE works correctly against all major capture methods:
- GDI BitBlt—window is absent.
- Desktop Duplication (DXGI)—window is absent (in Windows 11 24H2, there's a quirk where
AcquireNextFramewakes up on hidden window updates, but no content is passed). - Windows.Graphics.Capture (WGC)—window is absent.
The sole exception is PrintWindow. This method bypasses DWM and asks the window to render directly into the specified DC, ignoring the flag. However, most screen-sharing apps (Zoom, Teams, Discord, OBS) don't use PrintWindow.
Key limitation: WDA_EXCLUDEFROMCAPTURE is only supported starting with Windows 10 20H1 (May 2020 Update). On older versions, including LTSC, the function returns ERROR_INVALID_PARAMETER. In those cases, fall back to WDA_MONITOR, which shows a black rectangle in captures.
There's also an undocumented attribute WCA_EXCLUDED_FROM_DDA via SetWindowCompositionAttribute, which excludes the window only from Desktop Duplication API. In practice, it's unnecessary since WDA_EXCLUDEFROMCAPTURE covers it more comprehensively.
macOS up to 14: sharingType as a Reliable Mechanism
On macOS up to version 14, the NSWindow property sharingType was used:
window.sharingType = .none
Available values:
.readWrite—window is readable and modifiable by other processes (rarely used)..readOnly—window is capturable (default)..none—window is excluded from capture.
This flag worked through WindowServer, which handles compositing via Core Graphics. Up to and including macOS 14 Sonoma, sharingType = .none effectively blocked capture via both legacy APIs (CGWindowListCreateImage) and the new ScreenCaptureKit (introduced in macOS 12.3).
Electron and Tauri used this mechanism under the hood for their setContentProtection(true) methods.
macOS 15 Sequoia: Regression in ScreenCaptureKit
With macOS 15 Sequoia, Apple changed WindowServer's architecture: now compositing assembles all visible windows into a single framebuffer, and ScreenCaptureKit captures that directly. As a result, sharingType = .none no longer affects SCStream.
An official Apple representative confirmed in Developer Forums: “At this time there are no public APIs for preventing screen capture”.
Key nuance: legacy APIs (CGWindowListCreateImage) still respect sharingType = .none. This creates a split in behavior across apps:
- Zoom (current), Teams, QuickTime, system screenshots (Cmd+Shift+3/4/5), newer OBS versions—use ScreenCaptureKit → window is visible.
- Google Chrome (
getDisplayMedia), Google Meet in Chrome, older OBS versions—use CoreGraphics → window is hidden.
This is a fundamental regression for products needing screen capture protection. Without private APIs, there's no solution—and using them guarantees App Store review rejection and breakage on OS updates. Alternatives include limiting support to macOS 14 and earlier, or behavioral methods: auto-minimizing the window at screen share start, detecting SCStream activity via system signals.
Linux: X11 vs Wayland—Two Different Worlds
On Linux, the situation varies drastically depending on the display server.
X11—architecturally doesn't support window protection from capture. Any client with display access can call XGetImage or XCompositeNameWindowPixmap to grab pixels from any window. Compositing extensions (Picom, KWin, Mutter) may add partial protection, but no cross-WM solution exists.
Wayland—built on isolation principles: clients can't read others' surfaces. Screen capture is only possible via xdg-desktop-portal + PipeWire, and only with explicit user consent. Window protection is handled at the protocol level—if a window doesn't grant access, it won't appear in captures. This makes Wayland far safer than X11, but requires apps to implement protocols correctly.
Key Takeaways
SetWindowDisplayAffinitywithWDA_EXCLUDEFROMCAPTURE—de facto standard on Windows for fully excluding a window from capture. Works from Windows 10 May 2020 Update.- On macOS 15 Sequoia,
sharingType = .noneno longer works against ScreenCaptureKit—this is an Apple architectural change with no public workaround. - On Linux, X11 makes window protection impossible by protocol design; Wayland handles it at the protocol level.
PrintWindowon Windows bypasses protection, but isn't used in popular screen-sharing apps.- Electron and Tauri leverage these APIs under the hood—their behavior depends on platform and OS version.
— Editorial Team
No comments yet.