Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If Pillow’s ImageGrab or a Win32 BitBlt routine saves the desktop wallpaper while an application window is missing, first identify what you meant to capture. A full desktop grab, a screen rectangle, and a specific window use different paths. For layered content, test ImageGrab.grab(include_layered_windows=True) (or the Win32 CAPTUREBLT flag). For one window, use Pillow’s window=hwnd argument when running a version that supports it. If the window is still absent, investigate Windows display-affinity capture exclusion and application-specific rendering. None of these switches guarantees capture of every fullscreen, protected, minimized, or unusual rendering path.

What a background-only screenshot actually tells you

The symptom is underdetermined: it does not prove that Pillow or BitBlt is broken. It usually means the code captured a different target than intended, omitted layered content, or encountered a window that does not permit ordinary desktop capture. A fullscreen game, video surface, hardware-accelerated application, minimized window, or capture-protected window can behave differently from a normal overlapped desktop window.

Use the documented options as diagnostic tests, not as a promise that one flag fixes every application. Record the Windows build, Pillow version, exact call, target application, monitor arrangement and scaling, and whether the target is fullscreen, layered, minimized or occluded before drawing a conclusion.

1. Confirm the capture target

Full desktop

Calling ImageGrab.grab() with no bounding box captures the composed screen. It captures pixels currently presented by the desktop compositor; it is not a request to render an arbitrary application window independently of the desktop.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from PIL import ImageGrab

image = ImageGrab.grab()
image.save("desktop.png")

A screen rectangle

Pass bbox=(left, top, right, bottom) to select a rectangle in screen coordinates. The coordinates must correspond to the monitor layout Windows reports. With multiple monitors, use all_screens=True when you need the virtual desktop; the virtual desktop’s top-left coordinate can be negative.

from PIL import ImageGrab

image = ImageGrab.grab(
    bbox=(0, 0, 1920, 1080),
    all_screens=True,
)
image.save("region.png")

A rectangle that does not overlap the intended window will correctly contain the desktop background. Check the window’s actual position, DPI scaling and monitor before changing capture flags.

One particular window

If the requirement is “capture this HWND,” do not rely on a desktop rectangle. Current Pillow documentation exposes a Windows window argument for this purpose, and states that Windows support was added in Pillow 11.2.1. The value must be the handle of the intended top-level window.

from PIL import ImageGrab

# hwnd must be the target window's integer handle
image = ImageGrab.grab(window=hwnd)
image.save("window.png")

This is a window-capture path, not a guarantee that every application’s content is available. A protected or specialized rendering surface can remain absent.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

2. Include layered windows deliberately

Pillow’s include_layered_windows option is Windows-only and defaults to False. It was added in Pillow 6.1.0. Enable it when the missing material belongs to a layered window or is drawn above the source window.

from PIL import ImageGrab

image = ImageGrab.grab(include_layered_windows=True)
image.save("with-layered-windows.png")

For a region capture, combine the option with your bounding box:

from PIL import ImageGrab

image = ImageGrab.grab(
    bbox=(0, 0, 1920, 1080),
    include_layered_windows=True,
)
image.save("region-with-layered.png")

On the Win32 side, this behavior corresponds to the CAPTUREBLT raster-operation flag. Microsoft describes it as including “any windows that are layered on top of your window in the resulting image” in the BitBlt documentation. The flag addresses layered composition; it is not a universal fullscreen or hardware-rendering fix.

Using CAPTUREBLT with BitBlt

A typical GDI capture combines SRCCOPY with CAPTUREBLT. Keep the source and destination dimensions identical and ensure the source device context covers the intended screen coordinates.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import ctypes
from ctypes import wintypes

user32 = ctypes.windll.user32
gdi32 = ctypes.windll.gdi32

SRCCOPY = 0x00CC0020
CAPTUREBLT = 0x40000000
rop = SRCCOPY | CAPTUREBLT

# This fragment shows the flag combination; the rest of a complete
# capture must create compatible DCs and a bitmap for your target region.
# result = gdi32.BitBlt(dst_dc, 0, 0, width, height,
#                        src_dc, left, top, rop)

When debugging a complete implementation, verify every return value and call ctypes.get_last_error() immediately after a failing Win32 call. A successful BitBlt return does not establish that the application rendered capturable pixels into the source DC.

3. Check whether Windows excludes the window from capture

Windows provides SetWindowDisplayAffinity so a top-level window can control where its content is displayed. The WDA_EXCLUDEFROMCAPTURE value makes the window absent from capture. Microsoft documents support for that value beginning with Windows 10, version 2004; on earlier versions it behaves as WDA_MONITOR.

This setting is normally controlled by the application that owns the window. A screenshot program should treat it as a diagnostic possibility, not assume it can override another application’s policy. The API also requires Desktop Window Manager composition, and Microsoft’s documentation warns that there is no guarantee that display-affinity functions will strictly protect windowed content in every circumstance. See the SetWindowDisplayAffinity documentation.

What you can and cannot infer

  • If a normal window appears but one application is consistently missing, that application’s capture policy or rendering path is a stronger suspect than a global Pillow failure.
  • If enabling layered capture changes the result, the original call was not including a relevant layered surface.
  • If both desktop and HWND captures omit the same content, investigate display affinity, fullscreen behavior, minimization and hardware-accelerated presentation.
  • Do not claim that changing another process’s affinity setting is supported or safe.

4. A repeatable diagnostic procedure

  1. Write down the goal. Choose full virtual desktop, one monitor, a rectangle, or one HWND. Do not compare results from different targets as though they were equivalent.
  2. Check versions. Run python -c "import PIL; print(PIL.__version__)". Use a Pillow release that supports the option you are testing; the documented Windows window argument requires Pillow 11.2.1 or newer.
  3. Capture a known ordinary window. If a basic Notepad-style window appears, the capture pipeline is functioning for ordinary desktop content.
  4. Repeat with layered inclusion. Test include_layered_windows=True, or add CAPTUREBLT to a direct BitBlt call.
  5. Test the HWND path. Supply the intended handle with ImageGrab.grab(window=hwnd) when your Pillow version supports it.
  6. Check window state. Restore the window, bring it to the foreground, and test while it is visible. Note whether it is fullscreen, minimized, covered, or spanning monitors.
  7. Investigate capture exclusion. If only one application disappears, ask whether it intentionally uses display affinity or another capture-protection mechanism.
  8. Collect reproducible facts. Preserve the exact code, flags, bounding box, HWND, Windows build, Pillow version, GPU/display setup and target application version before seeking application-specific help.

Common failures and targeted fixes

Symptom Likely issue Next test
Wallpaper appears, target is outside the image Wrong rectangle, monitor origin or DPI coordinate Print the window rectangle and use a virtual-desktop capture with all_screens=True.
Overlays or translucent windows are missing Layered windows excluded Set include_layered_windows=True or use CAPTUREBLT.
Only one protected app is absent Display affinity or app-specific capture protection Check the app’s documented behavior; do not assume Pillow can override it.
window raises an argument error Pillow is older than the documented Windows window-capture support Upgrade Pillow or use a screen/region path appropriate to your version.
HWND capture returns an unexpected window Wrong handle, child handle, or stale handle Resolve the current top-level HWND and verify its title/process before capture.
BitBlt fails or returns an empty bitmap Invalid DC, bitmap or dimensions Check every Win32 return value, dimensions and selected bitmap; inspect the last error immediately.
Fullscreen content remains absent after CAPTUREBLT Rendering path is not ordinary layered desktop composition Test window capture, windowed mode and an application-supported capture interface; CAPTUREBLT is not universal.

Performance, reliability and security considerations

  • Large regions cost more. Capturing the entire virtual desktop allocates and copies more pixels than a window-sized rectangle. Limit the region when you only need one area.
  • Do not infer success from a file being written. Open the image and inspect its dimensions and representative pixels; a valid PNG can still contain only the background.
  • Timing matters. Capture after the target is restored and rendered. If an application is still switching modes or presenting frames, repeat the capture and record the state rather than treating one frame as definitive.
  • Respect privacy controls. Display-affinity settings exist to keep content out of capture. Do not bypass a third-party application’s protection without authorization.
  • Multiple monitors need coordinate discipline. Negative virtual-screen coordinates and per-monitor scaling can make an apparently correct rectangle point at a different desktop area.

Or skip the browser setup

If your actual goal is a dependable URL screenshot rather than debugging a local Windows capture path, ScreenshotNeo provides a one-request website screenshot API. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed as clean shots, and the response identifies the result with X-Page-Verdict and X-Billed headers. It also offers an MCP server with take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

See the ScreenshotNeo API documentation for all options. A minimal call is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The same request in Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

And Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Every plan includes the same features: full-page lazy-image loading, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF controls, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage information and an OpenAPI specification. The parameter names used by other screenshot APIs also work, which can simplify migration.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; yearly billing provides two months free. Create a free ScreenshotNeo account to try it without a card.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

When to stop changing flags

Stop treating the issue as a generic Pillow setting when ordinary windows capture correctly, layered inclusion has been tested, the HWND and screen paths agree, and the same application remains absent. At that point the decisive evidence is the target application’s rendering and capture policy, plus the exact Windows build and display configuration. Provide those facts with a minimal reproducible script instead of cycling through unrelated raster-operation constants.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Does include_layered_windows=True capture every fullscreen game?

No. It targets layered-window composition. Fullscreen or hardware-presented content may use a different path and can remain unavailable.

Can I force another application’s WDA_EXCLUDEFROMCAPTURE setting off?

The setting is controlled by the owning application. Treat it as a diagnostic explanation, not as a supported override for third-party windows.

Why can a valid image file still be useless?

Image encoding can succeed even when the source pixels are only the desktop. Validate the target rectangle, window state and captured content separately.

Which Pillow version supports the window argument on Windows?

Pillow’s current documentation says Windows support for ImageGrab.grab(window=...) was added in Pillow 11.2.1.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.