ScreenshotNeo

BlogScreenshots on your device

How to Take Desktop Screenshots in C#

Capture a screen, window, or selected region in C# with CopyFromScreen and Windows.Graphics.Capture, then save reliable PNG files.

By the ScreenshotNeo team29 September 20268 min read

How to Take Desktop Screenshots in C#

Direct answer: for a known rectangle in a Windows Forms or other Windows desktop process, create a Bitmap, create a Graphics object from it, and call Graphics.CopyFromScreen. The source point is in screen coordinates, the destination point is normally (0, 0) inside the bitmap, and the size is the rectangle to copy. For a user who must choose a display or application window through secure system UI, use Windows.Graphics.Capture and GraphicsCapturePicker.

This guide targets Windows desktop applications. Neither API is a cross-platform screenshot solution for macOS or Linux. It covers still images, not a complete recurring video-capture pipeline.

How do I take a screenshot in C#?

Start with CopyFromScreen when your program already knows the screen coordinates. Add the System.Drawing.Common support appropriate to your Windows desktop target and dispose every Bitmap and Graphics instance you create.

using System;
using System.Drawing;
using System.Drawing.Imaging;

public static class ScreenShot
{
    // screenOrigin uses desktop screen coordinates.
    // The caller owns the returned bitmap and must dispose it.
    public static Bitmap CaptureRegion(Point screenOrigin, Size size)
    {
        if (size.Width <= 0 || size.Height <= 0)
            throw new ArgumentOutOfRangeException(nameof(size));

        var bitmap = new Bitmap(size.Width, size.Height,
            PixelFormat.Format32bppArgb);

        try
        {
            using var graphics = Graphics.FromImage(bitmap);
            graphics.CopyFromScreen(
                screenOrigin,
                Point.Empty,
                size,
                CopyPixelOperation.SourceCopy);
            return bitmap;
        }
        catch
        {
            bitmap.Dispose();
            throw;
        }
    }

    public static void SaveRegion(string path, Point origin, Size size)
    {
        using var bitmap = CaptureRegion(origin, size);
        bitmap.Save(path, ImageFormat.Png);
    }
}

// Example: capture a 1280x720 rectangle whose upper-left corner is (0, 0).
ScreenShot.SaveRegion("desktop.png", new Point(0, 0), new Size(1280, 720));

CopyFromScreen transfers visible pixel color data from the source rectangle to the destination drawing surface. Microsoft documents that the operation can throw a Win32Exception when the copy fails, so production code should catch and log that exception at the application boundary. See the CopyFromScreen API documentation.

How do I capture the whole screen in C#?

Use the bounds of the display you intend to capture. Do not assume every monitor starts at coordinate (0, 0): Windows permits monitors to sit to the left or above the primary display, which produces negative coordinates. In a WinForms application, Screen.PrimaryScreen.Bounds captures the primary display.

Screen coordinates determine which desktop rectangle is copied into the bitmap.
Screen coordinates determine which desktop rectangle is copied into the bitmap.
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

public static void SavePrimaryScreen(string path)
{
    var bounds = Screen.PrimaryScreen.Bounds;
    using var bitmap = new Bitmap(bounds.Width, bounds.Height,
        PixelFormat.Format32bppArgb);
    using (var graphics = Graphics.FromImage(bitmap))
    {
        graphics.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size,
            CopyPixelOperation.SourceCopy);
    }
    bitmap.Save(path, ImageFormat.Png);
}

public static void SaveAllDisplays(string path)
{
    var bounds = SystemInformation.VirtualScreen;
    using var bitmap = new Bitmap(bounds.Width, bounds.Height,
        PixelFormat.Format32bppArgb);
    using (var graphics = Graphics.FromImage(bitmap))
    {
        graphics.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size,
            CopyPixelOperation.SourceCopy);
    }
    bitmap.Save(path, ImageFormat.Png);
}

The virtual-screen rectangle includes all attached displays. Its origin can be negative, while the destination bitmap always starts at (0, 0). If a monitor is disconnected between reading its bounds and copying pixels, retry or report the failure instead of writing a misleading partial image.

How do I capture a window or a selected area?

Known window or rectangle with CopyFromScreen

For a WinForms control or form, obtain its screen position with PointToScreen(Point.Empty), then use its client size. This captures what is visible on the desktop, including occlusion; it does not magically render a minimized or covered window.

using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

public static void SaveFormClientArea(Form form, string path)
{
    var origin = form.PointToScreen(Point.Empty);
    var size = form.ClientSize;
    using var bitmap = new Bitmap(size.Width, size.Height,
        PixelFormat.Format32bppArgb);
    using (var graphics = Graphics.FromImage(bitmap))
    {
        graphics.CopyFromScreen(origin, Point.Empty, size,
            CopyPixelOperation.SourceCopy);
    }
    bitmap.Save(path, ImageFormat.Png);
}

For a selected area, let the user drag a rectangle in an overlay form, convert the overlay coordinates to screen coordinates, normalize negative-width or negative-height drags, and pass the resulting screen origin and size to CaptureRegion. Validate that the rectangle has non-zero dimensions before creating the bitmap.

User-selected display or application window

Windows.Graphics.Capture is the modern Windows path when the user should choose a display or app window. Microsoft describes secure system UI for this selection and a yellow border around the actively captured item. Check support at runtime, handle cancellation, and then connect the selected GraphicsCaptureItem to a Direct3D frame pool and capture session. The API supports Windows desktop devices and Windows Mixed Reality immersive headsets. Read Microsoft’s screen capture guide and the GraphicsCapturePicker documentation.

using Windows.Graphics.Capture;

if (!GraphicsCaptureSession.IsSupported())
    throw new PlatformNotSupportedException("Windows.Graphics.Capture is unavailable.");

var picker = new GraphicsCapturePicker();
// In WinUI 3, initialize picker with the desktop app window handle
// before calling PickSingleItemAsync (see Microsoft documentation).
var item = await picker.PickSingleItemAsync();
if (item is null)
{
    // The user pressed Cancel or closed the picker.
    return;
}

// Create a Direct3D11 device, a Direct3D11CaptureFramePool using
// item.Size and the desired pixel format, then a GraphicsCaptureSession.
// Process frames from FrameArrived for a snapshot or a stream.

The picker returns an item; it does not itself save a PNG. A still snapshot requires waiting for a frame, copying its texture into a CPU-readable surface, and encoding that surface with an image library. A recurring capture pipeline must handle frame arrival, back-pressure, session closure, and device loss.

How do I save a screenshot as a PNG?

Call Bitmap.Save(path, ImageFormat.Png) while the bitmap is still alive. PNG is lossless and preserves sharp text, but files can be larger than JPEG or WebP. Use an explicit format rather than relying on a filename extension.

Windows.Graphics.Capture lets the user choose a display or app window before frames are delivered.
Windows.Graphics.Capture lets the user choose a display or app window before frames are delivered.
using var bitmap = ScreenShot.CaptureRegion(new Point(100, 100),
                                             new Size(800, 600));
bitmap.Save("capture.png", ImageFormat.Png);
// bitmap is disposed here

To save JPEG, use ImageFormat.Jpeg and an encoder with a chosen quality. Always write to a temporary file and rename it when producing files consumed by another process; this prevents readers from opening a half-written image.

CopyFromScreen options and edge cases

Concern Guidance
Source point Upper-left screen coordinate; may be negative on a multi-monitor desktop.
Destination point Usually Point.Empty, the upper-left of the bitmap.
Size Must be positive and fit the intended region.
Copy operation SourceCopy copies source pixels directly; choose deliberately if using another raster operation.
DPI scaling Per-monitor scaling can make logical control sizes differ from physical pixels. Make the process DPI-aware and verify the coordinate conversion on each target system.
Occlusion CopyFromScreen reads the visible desktop. Covered, minimized, locked, or protected content may not be available.
Security surfaces DRM or protected windows can appear black or restricted by Windows.

Microsoft’s WinForms printing example also uses a form’s location and size with CopyFromScreen; treat that as an example of copying a visible form, not as a universal window-capture architecture.

HDR, pixel formats, and image quality

The simple sample is SDR-oriented and uses 8-bit BGRA-compatible pixels. Microsoft notes that HDR content can look washed out when forced through an 8-bit BGRA pipeline. For HDR, carry a 16-bit floating-point format such as DXGI_FORMAT_R16G16B16A16_FLOAT through capture and destination surfaces, then choose an HDR-capable file format or apply tone mapping when producing SDR output. This matters primarily to Windows.Graphics.Capture and Direct3D pipelines; it is not required for an ordinary SDR PNG.

Troubleshooting

“The screenshot is black”

Check whether the region is covered, minimized, protected, or rendered with a hardware surface unavailable to desktop copying. Bring the window to the foreground for a visible desktop capture, or use Windows.Graphics.Capture for a user-selected app window. Protected content may remain unavailable.

Wrong monitor or clipped image

Log the source origin and size. Use Screen.Bounds or SystemInformation.VirtualScreen instead of hard-coded dimensions, and account for negative virtual-screen coordinates and DPI scaling.

Win32Exception from CopyFromScreen

The desktop or device may have changed, the rectangle may be invalid, or the process may lack a usable interactive desktop. Catch the exception, dispose the bitmap, and retry only for transient device or display changes.

Picker does not open in WinUI 3

Initialize GraphicsCapturePicker with the owning desktop window handle before PickSingleItemAsync. Also check GraphicsCaptureSession.IsSupported() and handle a null result as cancellation.

PNG cannot be opened

Ensure the stream or bitmap remains alive until encoding finishes, dispose resources after saving, and use an atomic temporary-file rename if another process reads the output.

Performance, reliability, and cost

A single CopyFromScreen call is appropriate for occasional stills. Large multi-monitor bitmaps consume memory proportional to width × height × bytes per pixel; release them promptly. For repeated frames, avoid allocating a new bitmap for every frame, use a bounded queue, and stop the session cleanly when the consumer falls behind. Windows.Graphics.Capture is the better foundation for a recurring frame pipeline, but it adds Direct3D setup and device-loss handling.

Desktop APIs have no network charge, but they depend on an interactive Windows session and the pixels currently visible to that session. If your job runs on a server, in a service, or in a CI worker without a desktop, use a browser or remote-rendering service instead.

Or skip the browser setup

If the thing you need is a website screenshot rather than the pixels on a local Windows desktop, ScreenshotNeo returns a PNG, JPEG, WebP, or PDF from one GET request. Its cleanup steps accept cookie and consent banners before capture and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.

See the ScreenshotNeo API documentation for all options, including full-page lazy-image loading, CSS-element capture, dark mode, device presets, retina scale, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous webhooks, bulk capture, usage data, and the OpenAPI specification.

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 fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo includes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Plans include 1,000 free shots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account.

FAQ

Can I capture a hidden WinForms window?

Not reliably with CopyFromScreen; it copies visible desktop pixels. A hidden or minimized window may need an application-specific rendering approach.

Does Windows.Graphics.Capture work on Linux?

No. The documented API is for supported Windows desktop devices and Windows Mixed Reality immersive headsets.

Should I use PNG or JPEG?

Use PNG for sharp text and lossless output. Use JPEG when smaller files matter more than exact pixel fidelity.

Is a screenshot one frame or a video?

A bitmap saved from CopyFromScreen is a still. Windows.Graphics.Capture can provide a frame stream, but you must build the frame-processing and encoding pipeline yourself.