Use GetDC, a compatible memory DC, a compatible bitmap, and BitBlt with SRCCOPY. For a full desktop capture, call GetDC(NULL); for a window client area, call GetDC(hwnd). Create the bitmap from the original source DC, select it into the memory DC, copy the pixels, then save the resulting HBITMAP as a DIB or BMP. Add CAPTUREBLT when layered windows must appear. Check every return value and release every GDI object.
What BitBlt captures—and what it does not
Microsoft defines BitBlt as a transfer of the color data for a pixel rectangle from one device context (DC) to another. It accepts DC handles, coordinates, dimensions, and a raster-operation code; it does not accept an HBITMAP as the source. A screenshot therefore has two parts: obtaining pixels in a source DC and providing a destination bitmap selected into a destination DC.
The source scope is determined by GetDC:
GetDC(NULL)returns a DC for the entire screen.GetDC(hwnd)returns a DC for the specified window’s client area, excluding its non-client frame, title bar, and borders.
For normal copying use SRCCOPY. Combine it with CAPTUREBLT (bitwise OR) to include layered windows drawn above the source. CAPTUREBLT is intended for display-style DCs and generally cannot be used with printing DCs.
Complete Win32 C++ example: capture the desktop to a BMP
The following program captures the virtual screen and writes a 32-bit BMP named screenshot.bmp. It uses the virtual-screen metrics so multiple monitors, including monitors positioned to the left or above the primary display, are included.
#1 Best Overall
- 14" diagonal, 1366x768 resolution, HD BrightView LED, Glossy NON-TOUCH Display
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <wingdi.h>
#include <cstdio>
static bool SaveBitmapFile(const wchar_t* path, HBITMAP bitmap, HDC referenceDC,
int width, int height) {
BITMAPINFOHEADER bi{};
bi.biSize = sizeof(bi);
bi.biWidth = width;
bi.biHeight = -height; // top-down DIB: first row is the top row
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
BITMAPINFO bmi{};
bmi.bmiHeader = bi;
void* pixels = nullptr;
HBITMAP dib = CreateDIBSection(referenceDC, &bmi, DIB_RGB_COLORS,
&pixels, nullptr, 0);
if (!dib || !pixels) return false;
HDC dibDC = CreateCompatibleDC(referenceDC);
if (!dibDC) {
DeleteObject(dib);
return false;
}
HGDIOBJ old = SelectObject(dibDC, dib);
bool copied = BitBlt(dibDC, 0, 0, width, height, referenceDC, 0, 0, SRCCOPY) != 0;
SelectObject(dibDC, old);
DeleteDC(dibDC);
if (!copied) {
DeleteObject(dib);
return false;
}
BITMAPFILEHEADER fileHeader{};
fileHeader.bfType = 0x4D42; // "BM"
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
fileHeader.bfSize = fileHeader.bfOffBits + width * height * 4;
HANDLE file = CreateFileW(path, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, nullptr);
if (file == INVALID_HANDLE_VALUE) {
DeleteObject(dib);
return false;
}
DWORD written = 0;
bool ok = WriteFile(file, &fileHeader, sizeof(fileHeader), &written, nullptr) &&
WriteFile(file, &bi, sizeof(bi), &written, nullptr) &&
WriteFile(file, pixels, width * height * 4, &written, nullptr);
CloseHandle(file);
DeleteObject(dib);
return ok;
}
int wmain() {
const int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
const int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
const int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
const int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
if (width <= 0 || height <= 0) return 1;
HDC source = GetDC(nullptr);
if (!source) return 2;
HDC memory = CreateCompatibleDC(source);
if (!memory) {
ReleaseDC(nullptr, source);
return 3;
}
HBITMAP bitmap = CreateCompatibleBitmap(source, width, height);
if (!bitmap) {
DeleteDC(memory);
ReleaseDC(nullptr, source);
return 4;
}
HGDIOBJ old = SelectObject(memory, bitmap);
DWORD rop = SRCCOPY | CAPTUREBLT;
BOOL copied = BitBlt(memory, 0, 0, width, height, source, x, y, rop);
DWORD error = copied ? ERROR_SUCCESS : GetLastError();
SelectObject(memory, old);
bool saved = copied && SaveBitmapFile(L"screenshot.bmp", bitmap, source, width, height);
DeleteObject(bitmap);
DeleteDC(memory);
ReleaseDC(nullptr, source);
if (!saved) {
std::fwprintf(stderr, L"Capture failed (error %lu)n", error);
return 5;
}
return 0;
}
Build it as a Unicode Windows application and link the normal User32 and GDI32 libraries (the standard Windows headers and libraries provide these APIs). The program returns a nonzero code on failure and prints the GetLastError value from BitBlt. The sample first captures into a device-dependent bitmap (DDB), then copies into a top-down 32-bit DIB so the file has predictable pixel layout and no bottom-up row reversal.
Implementing the capture pipeline correctly
1. Choose the source DC and rectangle
For the desktop, use the virtual-screen origin and dimensions shown in the example. For a single monitor, use that monitor’s coordinates and size. For a window client area, call GetClientRect for width and height, then pass GetDC(hwnd) as the source and source coordinates beginning at zero. A client capture intentionally omits the non-client frame. Capturing the complete frame requires a different window-capture approach and is not supplied by GetDC(hwnd)‘s client DC.
2. Create the destination from the source DC
Call CreateCompatibleDC(source), then CreateCompatibleBitmap(source, width, height). Do not create the bitmap from a newly created memory DC: a new memory DC starts with a 1-by-1 monochrome bitmap, and using it as the compatibility reference can produce a monochrome result. Select the bitmap into the memory DC and retain the object returned by SelectObject so it can be restored.
Rank #2
- 1.1 GHz (boost up to 2.4GHz) Intel Celeron N5030 Quad-Core
- 4GB DDR4 System Memory; 128GB Solid State Drive
- 11.6" HD (1366 x 768) Multi-Touch Display
- Combo headphone/microphone jack - Noble Wedge Lock slot - HDMI; 2 USB 3.1 Gen 1
- Windows 11 Pro
3. Copy with the appropriate raster operation
BitBlt(memory, 0, 0, width, height, source, sourceX, sourceY, SRCCOPY) performs a direct pixel copy. Use SRCCOPY | CAPTUREBLT when overlays implemented as layered windows must be visible. The source and destination rectangles may have different coordinates, but their transfer dimensions must be valid for the selected bitmap.
4. Convert or encode the pixels
A DDB is convenient for GDI operations but is tied to the device format. For image encoding, create a BITMAPINFO for a 32-bit DIB and call GetDIBits, SetDIBits, or use a DIB section as in the sample. A top-down DIB has a negative biHeight, which keeps the first row at the top. Once pixels are in a DIB, pass them to the image encoder or write a BMP.
Full screen, window, layered, and multi-device choices
| Goal | Source and dimensions | Important detail |
|---|---|---|
| Entire desktop | GetDC(NULL); virtual-screen metrics |
Use virtual origin for multi-monitor layouts. |
| Window client area | GetDC(hwnd); GetClientRect |
Frame and title bar are excluded. |
| Layered overlays included | Any display/window source with SRCCOPY | CAPTUREBLT |
Not generally suitable for printing DCs. |
| Portable pixel buffer | DIB or DIB section after the blit | Prefer this when encoding, processing, or crossing devices. |
| Different source and destination devices | Capture to a DIB, then use SetDIBits or StretchDIBits |
Direct transfers between DCs representing different devices fail. |
Error handling and resource cleanup
Check for null from GetDC, CreateCompatibleDC, and CreateCompatibleBitmap. Check the Boolean result from BitBlt; zero indicates failure, after which GetLastError may provide extended information. Check file creation and writes separately from capture so an encoding failure is not mistaken for a display failure.
Rank #3
- 256 GB SSD of storage.
- Multitasking is easy with 16GB of RAM
- Equipped with a blazing fast Core i5 2.00 GHz processor.
Cleanup order matters. Restore the previously selected bitmap before deleting it. Then call DeleteObject for the bitmap, DeleteDC for a DC created by CreateCompatibleDC, and ReleaseDC for a DC returned by GetDC. GDI handles are limited; leaking one DC or bitmap per frame will eventually make a long-running capture process fail.
Why a BitBlt screenshot is black, incomplete, or monochrome
Black or empty output
- Wrong source coordinates: verify the virtual-screen origin, especially when a monitor is left or above the primary display.
- Invalid dimensions: ensure width and height are positive and fit the selected bitmap.
- Protected or composed content: some applications deliberately prevent ordinary GDI capture. BitBlt cannot override that policy.
- Layered overlay missing: retry with
SRCCOPY | CAPTUREBLTfor a display DC.
Monochrome bitmap
Create the compatible bitmap with the original source DC, not the memory DC. This is the classic 1-by-1 monochrome-memory-DC mistake.
BitBlt returns zero
Log GetLastError() immediately, confirm that all handles are valid, and check whether the source and destination represent different devices. If they do, move the pixels through a DIB and use SetDIBits or StretchDIBits rather than a direct cross-device transfer.
Rank #4
- EFFORTLESS EVERYDAY PERFORMANCE: Powered by Intel Celeron N4020 processor and Windows 11 Home system, delivering reliable, low-power efficiency for daily tasks like document editing, email, online classes, and web browsing
- 15.6-INCH FULL HD DISPLAY: Enjoy immersive visuals on the 15.6" FHD (1920x1080) anti-glare screen with micro-edge bezels. Delivers clear details and comfortable viewing for long study sessions, working on spreadsheets, and video playback
- RESPONSIVE MULTITASKING & STORAGE: Built with 4GB LPDDR4 RAM and 128GB eMMC storage for smooth daily essential use. Expand your storage by up to 1TB via the integrated TF card slot to easily store movies, photos, and working files
- ADVANCED CONNECTIVITY: Outfitted with 2x Full-Featured Type-C ports for data transfer, fast charging, and dual-monitor output, alongside 2x USB 3.2 Gen1 ports and a 3.5mm audio jack for complete peripheral compatibility
- LIGHTWEIGHT & SILENT OPERATION: Slim and portable for effortless travel or commuting. Features a 1MP HD webcam for remote meetings, 38Wh battery with 45W Type-C fast charging, and a fanless silent design for peaceful work environments.
Window capture is cropped
GetDC(hwnd) targets the client area by definition. If the requirement includes the non-client frame, calculate and capture the appropriate screen rectangle instead of assuming the client DC contains it.
Performance and reliability practices
- Reuse a compatible DC, bitmap, and DIB buffer when repeatedly capturing the same dimensions; recreate them only when the size or format changes.
- Keep the capture and encoding stages separate. A slow PNG encoder should not hold a source DC longer than necessary.
- Use a DIB section when downstream code needs direct pixel access; it avoids an extra readback copy.
- Handle display-topology changes: monitor additions, removals, DPI changes, and resolution changes can invalidate cached dimensions.
- Bound capture frequency and queue encoding work so GDI resources and memory do not grow without limit.
- Test with and without
CAPTUREBLT; layered-window support can change the captured composition and is not needed for every workload.
Or skip the browser setup
If your goal is a webpage image rather than the Windows desktop, ScreenshotNeo provides a single HTTP request and handles browser startup, page loading, and image output for you. Its clean-shot flow accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.
cURL (see the ScreenshotNeo documentation):
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)
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}`);
ScreenshotNeo also exposes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
Free tools Windows power users keep installed
One-click scans. No signup required.
FAQ
Does BitBlt capture a bitmap handle directly?
No. Both source and destination are device contexts; select the destination bitmap into its DC first.
Best Value
- WINDOWS 11 | STABLE PERFORMANCE: Powered by Intel Celeron N4020 processor and Windows 11 system, this laptop delivers stable performance for everyday computing tasks. It supports web browsing, online learning, document editing, email communication, and basic office work with optimized power efficiency, providing a practical and reliable experience for essential daily use for daily use.
- 15.6” FHD IPS DISPLAY: Features a 15.6-inch Full HD IPS display with narrow bezels, offering wider viewing angles and clearer image details compared to standard panels. The improved screen-to-body ratio enhances visual experience for study, reading, document work, and video playback, making it suitable for both productivity and entertainment use.
- 4GB DDR4 + 128GB eMMC STORAGE: Equipped with 4GB DDR4 memory and 128GB eMMC storage for everyday basics such as browsing, documents, email, and online learning platforms. The built-in TF card slot supports storage expansion up to 1TB, giving you more flexibility for files, photos, videos, and daily documents. TF card not included.
- CONNECTIVITY & PORTS: Includes 1× TF card slot, 2× USB 3.2 Gen1 ports, and 2× full-featured Type-C ports (USB 3.2 Gen1). The Type-C ports support data transfer, charging, and video output, enabling flexible connection with external devices such as monitors, storage, and peripherals for daily work and study use.
- LIGHTWEIGHT DESIGN | ONLINE COMMUNICATION: Designed with a slim, portable profile, this laptop is easy to carry for school, commuting, and travel. A built-in 1MP front camera supports online classes, video meetings, remote communication, and everyday conferencing. The 3300mAh battery works with the low-power system design to support practical daily use, while thermal optimization helps maintain quieter operation during extended tasks.
Should I always use CAPTUREBLT?
No. Add it when layered windows must be included and the source is a display-style DC. Keep plain SRCCOPY for ordinary captures.
Why use a DIB instead of a DDB?
A DIB gives your encoder and processing code a predictable pixel layout and is the appropriate bridge when source and destination devices differ.
Frequently Asked Questions
Can BitBlt capture another monitor?
Yes. Use the virtual-screen metrics and pass the virtual-screen origin and total dimensions to BitBlt; this covers monitors arranged left, right, above, or below the primary display.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Does GetDC(hwnd) include a window’s title bar?
No. It returns the window’s client-area DC. Capturing the frame requires a screen-coordinate rectangle that includes the non-client area.
What should I do when a protected application remains black?
Treat it as an application capture restriction. Ordinary BitBlt cannot bypass content that the application or display-composition path excludes.
Quick Recap
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.

