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

To capture one Windows application window with PowerShell, obtain that window’s handle (HWND), read its screen rectangle with Win32 GetWindowRect, copy the rectangle with System.Drawing.Graphics.CopyFromScreen, and save the bitmap as PNG. The script below is an illustrative API pattern: it is not presented as tested on every PowerShell or Windows version, and you must supply or discover the correct HWND.

Capture a specific window in PowerShell

The following script captures the window identified by $hwnd and writes window.png in the current directory. Set $hwnd to the handle of the window you want before running it.

  1. Open the target application and make sure the desired window is visible.
  2. Assign its HWND to $hwnd. A process can have several windows, so do not assume the process’s main handle is always the right one.
  3. Run the script in Windows PowerShell or PowerShell 7 on Windows with access to the desktop.
Add-Type -AssemblyName System.Drawing

if (-not ('WindowCaptureNative' -as [type])) {
    Add-Type @'
using System;
using System.Runtime.InteropServices;

public static class WindowCaptureNative {
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool GetWindowRect(
        IntPtr hWnd,
        out RECT rect
    );
}
'@
}

# Replace this with the HWND you intend to capture.
$hwnd = [IntPtr]0
if ($hwnd -eq [IntPtr]::Zero) {
    throw 'Set $hwnd to a valid window handle before running the script.'
}

$rect = New-Object WindowCaptureNative+RECT
if (-not [WindowCaptureNative]::GetWindowRect($hwnd, [ref]$rect)) {
    $errorCode = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
    throw "GetWindowRect failed. Win32 error: $errorCode"
}

$width = $rect.Right - $rect.Left
$height = $rect.Bottom - $rect.Top
if ($width -le 0 -or $height -le 0) {
    throw "Window bounds are empty: ${width}x${height}."
}

$outputPath = Join-Path $PWD 'window.png'
$bitmap = New-Object System.Drawing.Bitmap($width, $height)
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
try {
    $graphics.CopyFromScreen(
        $rect.Left,
        $rect.Top,
        0,
        0,
        $bitmap.Size
    )
    $bitmap.Save($outputPath, [System.Drawing.Imaging.ImageFormat]::Png)
    Write-Host "Saved $outputPath ($width x $height)."
}
finally {
    $graphics.Dispose()
    $bitmap.Dispose()
}

GetWindowRect returns screen coordinates. The lower-right coordinate is exclusive, so the dimensions are Right - Left and Bottom - Top. CopyFromScreen then copies those screen pixels into the bitmap. The result reflects what is currently rendered at those coordinates, not an independent copy of the application’s internal drawing surface.

How to find the correct HWND

Use a process’s main window handle

For a simple application with one obvious main window, you can inspect processes and use MainWindowHandle:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
PowerShell for Sysadmins: Workflow Automation Made Easy
  • Book - powershell for sysadmins: workflow automation made easy
  • Language: english
  • Binding: paperback
$process = Get-Process -Name notepad | Where-Object MainWindowHandle -ne 0 | Select-Object -First 1
if (-not $process) { throw 'No visible Notepad main window was found.' }
$hwnd = [IntPtr]$process.MainWindowHandle
$process | Select-Object Id, ProcessName, MainWindowTitle, MainWindowHandle

Use the displayed title and handle to confirm that you selected the intended instance. If two copies of an application are open, filter by MainWindowTitle, process ID, or another property rather than taking the first result.

When the main handle is not enough

Dialogs, tool windows, document windows, and secondary windows may not be represented by MainWindowHandle. In that case, use a window-inspection utility or write Win32 enumeration code that calls EnumWindows and records each top-level window’s handle and title. Validate the selected HWND before capturing; a zero or stale handle is not a valid target.

Window borders, DPI, and coordinate accuracy

Invisible resize borders

On Windows Vista and later, GetWindowRect can include invisible resize borders. That may make the image a few pixels larger than the visible frame. For visible-frame bounds, Microsoft documents the Desktop Window Manager attribute DWMWA_EXTENDED_FRAME_BOUNDS through DwmGetWindowAttribute.

Do not mix coordinate systems casually

DWM extended-frame bounds are not DPI-adjusted in the same way as GetWindowRect. If you obtain coordinates from one API and dimensions or scaling assumptions from the other, the capture can be offset or incorrectly sized. Pick one coordinate model and account for the process’s DPI-awareness and monitor scaling.

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.

What the script can and cannot capture

  • The target should be visible on the active desktop and positioned where the returned rectangle says it is.
  • A covered window can produce pixels from the window covering it, because the method copies the screen rather than the application surface.
  • A minimized window, a window on another virtual desktop, or a surface protected by the operating system may yield an empty or unexpected image.
  • Full-screen, hardware-overlay, remote-session, and DRM-protected content can have application-specific restrictions that this GDI+ method does not bypass.

Save JPEG or another image format

PNG is lossless and preserves text well. To save JPEG, change the output extension and image encoder:

$outputPath = Join-Path $PWD 'window.jpg'
$bitmap.Save($outputPath, [System.Drawing.Imaging.ImageFormat]::Jpeg)

For WebP or advanced compression, use an image library that supports that encoder after the capture. The coordinate and bitmap steps remain the same.

Make the capture safer for automation

Check the output file

After Save returns, verify that the file exists and has a nonzero length:

$item = Get-Item $outputPath -ErrorAction Stop
if ($item.Length -eq 0) { throw 'The screenshot file is empty.' }
Write-Host "Verified $($item.FullName), $($item.Length) bytes."

Handle failures and dispose resources

The try/finally block is important. GDI+ bitmap and graphics objects hold unmanaged resources; disposing them prevents leaks during repeated captures. Microsoft’s CopyFromScreen documentation notes that a Win32 exception can occur, so catch the exception at the calling level when a scheduled job must log and continue:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
try {
    # Invoke the capture function or script here
}
catch {
    Write-Error "Window capture failed: $($_.Exception.Message)"
    exit 1
}

Capture repeatedly

Resolve the HWND once only if the application window will not be recreated. If the program restarts, its old handle becomes invalid; resolve the process and handle again before each capture or batch.

Troubleshooting common problems

Symptom Likely cause Fix
“Set $hwnd…” or an empty handle error The variable is still zero or was never assigned. Discover the target handle, cast it to [IntPtr], and confirm it is nonzero.
GetWindowRect returns false The handle is invalid, the window was destroyed, or the call failed. Resolve the HWND again, test the window while it is open, and log Marshal.GetLastWin32Error().
Image has the wrong border size Invisible resize borders are included. Use DWM extended-frame bounds for a visible-frame crop and keep DPI handling consistent.
Image shows another window The target was covered at the screen coordinates. Bring the target to the foreground, uncover it, or use an application capture API designed to capture a surface rather than screen pixels.
Black, blank, or stale output The window is minimized, on another desktop, protected, or not yet rendered. Make it visible, wait for rendering, test on the active desktop, and consider Windows.Graphics.Capture for an integrated application workflow.
CopyFromScreen throws a Win32 exception Invalid dimensions, inaccessible display state, or a graphics failure. Check positive width and height, confirm the rectangle is on a usable display, and retain the disposal and exception handling.
PowerShell cannot load System.Drawing The runtime or platform does not provide the Windows graphics assembly as expected. Run on Windows with the appropriate .NET/PowerShell installation, or use a Windows capture API from an application.

Built-in and application-level alternatives

Snipping Tool for a one-off screenshot

For manual work, open Snipping Tool, choose a new snip, select Window, and click the target. Windows also exposes an ms-screenclip://capture/image?window protocol that can launch the interactive window mode. This is user-driven: it does not select an HWND automatically or provide a repeatable unattended job.

Windows.Graphics.Capture for an integrated app

Microsoft’s Windows.Graphics.Capture API family is intended for application code that needs a display or application-window capture surface. Its documented flow uses a system picker for user selection and supports Windows desktop devices and Windows Mixed Reality immersive headsets. It requires application integration, so it is not a PowerShell one-liner replacement for the script above.

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

Or skip the browser setup

If what you actually need is a webpage image rather than the pixels of a local desktop window, ScreenshotNeo provides a single HTTP request. It accepts cookie and consent banners as a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

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.

Read the parameter reference in the ScreenshotNeo documentation. cURL:

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

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)

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(`HTTP ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
await Bun.write('shot.webp', data);

ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Its Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is on every plan. Sign up for the free plan.

Choosing the right method

Need Best fit Why
Repeatable PowerShell job for a visible local window HWND + GetWindowRect + CopyFromScreen Scriptable and requires no interactive picker.
One manual capture Snipping Tool Window mode Fastest built-in workflow; no code or handle discovery.
Application-integrated display/window capture Windows.Graphics.Capture Designed for capture surfaces and picker-based app integration.
Automated webpage screenshots ScreenshotNeo Remote capture with consent cleanup, billing verdicts, and an MCP server.

Frequently Asked Questions

Does this PowerShell method capture a minimized window?

Not reliably. It copies pixels from the screen rectangle, so a minimized or non-rendered window can produce blank or unrelated output.

Why is my screenshot a few pixels wider than the visible window?

GetWindowRect may include invisible resize borders. Use DWM extended-frame bounds when you need the visible frame, while accounting for the different DPI behavior.

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

Can I use this script to capture a webpage without opening a browser?

No. The script captures a desktop window’s rendered screen pixels. For automated webpage capture, use a browser-based service such as ScreenshotNeo.

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.