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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

To capture a window that is behind another window, capture the target window itself—not a rectangle of the desktop. On Windows, Python can call Win32 PrintWindow through pywin32; ordinary screen-capture methods such as BitBlt, Pillow screen grabs, or an mss rectangle capture show the pixels currently visible, including the window covering your target. The important limitation is that PrintWindow asks the target application to render its window, so the result depends on whether that application supports the request. A minimized or GPU-rendered window is not guaranteed to work.

This guide shows the Windows method, a visible-window fallback that works from window geometry, and what changes on macOS and Linux. It distinguishes an inactive window that is still visible from one that is covered or minimized, because those cases need different capture methods.

Choose the capture method that matches the window

First decide what “background” means in your case. A window can be inactive but still visible, covered by another window, or minimized. Those states are not interchangeable: a desktop screenshot can capture visible pixels, but it cannot reconstruct pixels hidden behind another window.

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.
Window state What an ordinary screen-region capture returns What to use
Inactive, but visible The target window, as it appears on the desktop Capture its screen rectangle with a tool such as mss.
Covered by another window The covering window’s pixels where it overlaps the target Use a window-rendering or window-ID capture API, such as Windows PrintWindow, if the platform and application support it.
Minimized Usually no useful visible window rectangle Treat native rendering as best effort; restore the window or use an application-level export if possible.

On Windows, PrintWindow asks the application that owns the target window to render into a device context supplied by the caller. The request is delivered through window-printing messages. By contrast, BitBlt copies pixels between device contexts; when its source is the desktop, overlapping windows remain in the copied image. This distinction is the key to avoiding a screenshot of the wrong window.

Capture a covered window on Windows with Python

The example below finds a top-level window by its exact title, allocates a bitmap the size of its outer window frame, asks Windows to render into it, and saves a PNG. It does not activate or bring the target window to the front. The code uses PrintWindow with flag 0; rendering quality and completeness still depend on the target application.

Install the Windows dependencies

Run this in the Python environment that will execute the script:

py -m pip install pywin32 Pillow

Use a title that matches a real, open top-level window. In Windows, a window title may change as the document or page changes, and multiple windows can have similar titles. If the exact-title lookup does not find the window, use the enumeration alternative below.

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

Runnable capture script

import sys
import win32gui
import win32ui
from PIL import Image

def capture_window(title: str, output_path: str) -> None:
    hwnd = win32gui.FindWindow(None, title)
    if not hwnd:
        raise RuntimeError(f"No top-level window found with exact title: {title!r}")

    left, top, right, bottom = win32gui.GetWindowRect(hwnd)
    width, height = right - left, bottom - top
    if width <= 0 or height <= 0:
        raise RuntimeError(f"Window has invalid dimensions: {width}x{height}")

    # PrintWindow renders the target window into this memory device context.
    window_dc = win32gui.GetWindowDC(hwnd)
    source_dc = win32ui.CreateDCFromHandle(window_dc)
    memory_dc = source_dc.CreateCompatibleDC()
    bitmap = win32ui.CreateBitmap()
    bitmap.CreateCompatibleBitmap(source_dc, width, height)
    memory_dc.SelectObject(bitmap)

    try:
        result = win32gui.PrintWindow(hwnd, memory_dc.GetSafeHdc(), 0)
        if not result:
            raise RuntimeError("PrintWindow reported failure for this window")

        # Windows bitmap data is bottom-up; Pillow's raw decoder flips it upright.
        pixels = bitmap.GetBitmapBits(True)
        image = Image.frombuffer(
            "RGB", (width, height), pixels, "raw", "BGRX", 0, -1
        )
        image.save(output_path, format="PNG")
    finally:
        win32gui.ReleaseDC(hwnd, window_dc)
        memory_dc.DeleteDC()
        source_dc.DeleteDC()
        win32gui.DeleteObject(bitmap.GetHandle())

if __name__ == "__main__":
    if len(sys.argv) != 3:
        raise SystemExit('Usage: py capture_window.py "Exact window title" output.png')
    capture_window(sys.argv[1], sys.argv[2])

Save it as capture_window.py, then run, for example:

py capture_window.py "Untitled - Notepad" notepad.png

The output is the outer window frame reported by GetWindowRect, not a guaranteed crop of just the client area. Depending on the application and Windows rendering behavior, borders, title-bar elements, shadows, or other chrome may differ from a desktop screenshot. If you specifically need the client area, calculate its screen bounds with GetClientRect and ClientToScreen; however, note that PrintWindow still renders the window according to the target application’s behavior, so changing the rectangle alone does not make an unsupported application render correctly.

Finding a window when the title is not known exactly

FindWindow is convenient when the title is known, but it requires an exact match. To locate a candidate by partial title, enumerate top-level windows and inspect their titles. Then pass the selected handle to the capture routine instead of performing the lookup inside it.

import win32gui

def matching_windows(fragment: str):
    matches = []
    def visit(hwnd, _):
        if not win32gui.IsWindow(hwnd):
            return
        title = win32gui.GetWindowText(hwnd)
        if fragment.casefold() in title.casefold():
            matches.append((hwnd, title))
    win32gui.EnumWindows(visit, None)
    return matches

for hwnd, title in matching_windows("Notepad"):
    print(hwnd, repr(title))

Enumeration can return more than one match, including windows belonging to other processes. Inspect the title and choose the intended handle rather than silently capturing the first result. A window handle identifies a particular window instance; it may become invalid when the application closes or recreates that window.

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

Capture an inactive window that remains visible

If another window does not cover the target, a regular screen capture is simpler and often more faithful to what a person sees. PyWinCtl can locate windows and expose a client frame; turn that frame into left, top, width, and height, then capture that screen rectangle with mss. This captures desktop pixels, so it does not solve occlusion.

Install the libraries:

py -m pip install pywinctl mss

Example for a window that is visible and not covered:

import pywinctl as pwc
import mss
import mss.tools

matches = pwc.getWindowsWithTitle("Notepad")
if not matches:
    raise RuntimeError("No matching window found")

window = matches[0]
frame = window.getClientFrame()
if not frame:
    raise RuntimeError("Could not read the window client frame")

left, top, right, bottom = frame
region = {
    "left": left,
    "top": top,
    "width": right - left,
    "height": bottom - top,
}
with mss.mss() as screen:
    shot = screen.grab(region)
    mss.tools.to_png(shot.rgb, shot.size, output="visible-window.png")

Check your PyWinCtl version’s return shape if adapting this example: the required values are the client frame’s left, top, right, and bottom coordinates. Screen coordinates can also be affected by multiple monitors, display scaling, and negative coordinates for monitors arranged to the left or above the primary display. Use the coordinates returned by the window library rather than assuming the target starts at (0, 0).

Platform differences: macOS and Linux

macOS

Core Graphics can list windows and provide window IDs (CGWindowID), which can be used with a Core Graphics image-capture call or a Pillow path that accepts a window identifier. This is a window-ID-oriented approach, not a portable equivalent of the Windows code above. macOS privacy and screen-recording permissions can affect whether capture succeeds. Apple documents that CGWindowListCreate returns NULL when called outside a GUI security session or when no window server is running.

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

If a capture returns nothing, check that the Python process is running in a GUI session and that macOS has granted the relevant screen-recording permission. The exact permission prompt and controls can vary by macOS release and how the process is launched; do not assume that a script run in a service or remote session has the same desktop access as one run from a logged-in user’s desktop.

Linux: X11 and Wayland

X11 supports window-ID-based capture, and many Python window tools are designed around X11. Wayland intentionally restricts global window inspection and capture. PyWinCtl warns that getActiveWindow() and getAllWindows() are unreliable for many system applications under Wayland; its documentation also notes that WSL2 is unsupported. If background-window capture is essential, use an X11/XWayland session or a compositor-native portal or API supported by the desktop environment.

Do not assume that installing the same Python package makes the same code work across X11, Wayland, macOS, and Windows. The operating system’s window server, permissions, and target application’s rendering path determine whether an occluded window can be captured.

Covered, minimized, and GPU-rendered windows: what can fail

A covered window still exists, but its screen pixels are obscured. A minimized window is not simply a covered window: it may not appear in window enumeration, and the application may not render useful content until restored. PyWinCtl specifically warns that minimized windows may not appear in enumeration. Treat minimized-window capture as best effort and prefer restoring the window or using the application’s own export function when accuracy matters.

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

Some applications do not fully implement the window-printing messages used by PrintWindow. Others draw important content through GPU-backed or other application-specific surfaces that are not reproduced by the request. A successful API return is not proof that the image is visually correct: inspect the saved image for black regions, missing content, or absent window chrome.

Also keep the distinction between a capture error and a bad capture. A call can fail explicitly, return an image of the wrong size, or produce a valid PNG containing only black or incomplete pixels. For automated workflows, validate the result in the way appropriate to the application—at minimum, check that a file was saved and can be opened, and consider application-specific checks for expected content.

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

Troubleshooting common failures

  • “No top-level window found.” The title may not match exactly, the window may not be top-level, or the application may have closed. Print candidate titles with EnumWindows and select the intended handle.
  • PrintWindow returns false. The target or its current state may not support the request. Confirm the handle is valid; try the visible-region method if the window is unobscured, or use an application-level export. Do not substitute BitBlt and expect it to reveal covered pixels.
  • The PNG is black, blank, or missing part of the interface. The application may not render its content for PrintWindow, particularly with application-specific or GPU-rendered surfaces. Try a non-minimized state, inspect a visible capture for comparison, and use an export or supported application API if the output must be reliable.
  • The screenshot contains the window on top. The method captured the desktop rectangle, not the target window independently. Use a window-rendering or window-ID capture API supported by the platform and application.
  • The image is upside down or has strange colors. Check the bitmap channel order and orientation conversion when moving raw Windows bitmap bytes into Pillow. The example uses the BGRX decoder and bottom-up orientation; avoid treating raw bitmap memory as ordinary RGB bytes.
  • PyWinCtl cannot find a window on Linux. Check whether the desktop session is Wayland or X11. Under Wayland, global enumeration can be unreliable; use a supported compositor API or an X11/XWayland environment where appropriate.
  • macOS returns no window image. Confirm the process is in a GUI security session with a running window server, and review screen-recording privacy permissions.
  • The capture is cropped or includes unexpected borders. Decide whether you need the outer frame or client content. GetWindowRect describes the outer rectangle; client-frame geometry is different, and visible screen-region captures also depend on display scaling and monitor layout.

Performance, reliability, and cost considerations

These examples capture one window at a time and do not establish a speed or reliability benchmark. For occasional diagnostic screenshots, synchronous capture is straightforward. For repeated capture, avoid assuming that a window handle remains valid indefinitely: applications can close, reopen, or recreate windows. Re-query the target and handle failures instead of treating an earlier successful capture as proof that later ones will work.

Capturing a large frame requires allocating and transferring bitmap data, so image size affects memory and processing time. Capture only the region you need when using a visible screen-region method. For covered windows, however, cropping the desktop rectangle cannot recover hidden pixels; the target itself must render through an appropriate API. When a screenshot is needed for auditing or unattended automation, check the actual image and keep a fallback such as restoring the window or exporting from the application.

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

Or skip the browser setup

If what you actually need is a screenshot of a public website—not an arbitrary desktop application window—ScreenshotNeo can return an image or PDF from one API request. It does not capture a covered Windows, macOS, or Linux desktop window, so it is not a replacement for PrintWindow when the target is a local application.

For a website screenshot, install Requests with python -m pip install requests, then call the API as shown in the ScreenshotNeo documentation:

import requests

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

For website captures, ScreenshotNeo removes cookie or consent banners, newsletter popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are not billed. Its MCP server gives AI agents screenshot tools, and the free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Start with 1,000 free screenshots a month with no card.

Frequently Asked Questions

Does capturing a background window change focus or bring it to the front?

The Windows example calls PrintWindow without activating the window. Whether the target application renders complete content is separate from whether focus changes.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

Can I use ScreenshotNeo to capture a covered desktop application?

No. ScreenshotNeo captures web pages through its API; it does not capture local desktop windows or reveal content hidden behind another application.

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.