ScreenshotNeo

BlogScreenshots on your device

Why BitBlt Screenshots Are Black on Windows 10 and How to Fix Them

A black BitBlt capture can mean API failure, protected content, or a capture-tool issue. Diagnose each case with these Windows checks.

By the ScreenshotNeo team1 October 20267 min read

Short answer: a black BitBlt screenshot does not by itself prove that BitBlt failed. The target window may intentionally block capture, the capture application may have a problem with that rendering path, or BitBlt may have returned zero. Check the return value and GetLastError, test another window, and compare BitBlt with Windows Graphics Capture when your tool supports both.

1. What BitBlt actually does

BitBlt copies a rectangle of pixels from a source device context (DC) to a destination DC. Its return value is nonzero on success and zero on failure. When it returns zero, call GetLastError() immediately for extended error information.

A black result has three distinct explanations:

  • API failure: invalid or incompatible DCs, unsupported devices, or source transformations such as rotation or shear can make BitBlt fail.
  • Valid black/protected content: Windows can make a protected window appear black to screen-capture APIs.
  • Capture-tool behavior: the application may use a rendering path that BitBlt cannot read correctly even though another capture method works.

2. Diagnose the scope before changing anything

  1. Capture the full desktop.
  2. Capture two ordinary windows, such as File Explorer and Notepad.
  3. Capture the window that appears black.
  4. Record the Windows build, capture application and version, selected method, display/GPU, and whether the black result follows the window or the entire desktop.
What you observe Most useful next check
Only one application is black Check whether that application protects its content or uses special rendering.
Every target is black Log the BitBlt result and error, then inspect the capture application’s method and graphics stack.
BitBlt is black but Windows Graphics Capture works Use the working method if your application supports it.
Both methods are black and the desktop shows artifacts Investigate graphics-driver or hardware errors.

This is a diagnostic inference from Windows’ documented content-protection behavior, not a guarantee that one test identifies every cause.

3. Check BitBlt’s return value and error code

Do not save the bitmap and assume success. Check the result, dimensions, DC validity and the error code on failure. The following complete C++ example captures the primary screen into a 32-bit bitmap and writes screen.bmp.

#define UNICODE
#include <windows.h>
#include <cstdio>

int wmain() {
    HDC screen = GetDC(nullptr);
    if (!screen) {
        std::fprintf(stderr, "GetDC failed: %lu\n", GetLastError());
        return 1;
    }

    const int width = GetSystemMetrics(SM_CXSCREEN);
    const int height = GetSystemMetrics(SM_CYSCREEN);
    HDC memory = CreateCompatibleDC(screen);
    HBITMAP bitmap = CreateCompatibleBitmap(screen, width, height);
    if (!memory || !bitmap) {
        std::fprintf(stderr, "DC/bitmap creation failed: %lu\n", GetLastError());
        if (bitmap) DeleteObject(bitmap);
        if (memory) DeleteDC(memory);
        ReleaseDC(nullptr, screen);
        return 1;
    }

    HGDIOBJ old = SelectObject(memory, bitmap);
    SetLastError(ERROR_SUCCESS);
    BOOL copied = BitBlt(memory, 0, 0, width, height,
                         screen, 0, 0, SRCCOPY | CAPTUREBLT);
    if (!copied) {
        std::fprintf(stderr, "BitBlt failed: %lu\n", GetLastError());
        SelectObject(memory, old);
        DeleteObject(bitmap);
        DeleteDC(memory);
        ReleaseDC(nullptr, screen);
        return 1;
    }

    BITMAPINFOHEADER header{};
    header.biSize = sizeof(header);
    header.biWidth = width;
    header.biHeight = -height; // top-down image
    header.biPlanes = 1;
    header.biBitCount = 32;
    header.biCompression = BI_RGB;

    BITMAPFILEHEADER file{};
    file.bfType = 0x4D42; // BM
    file.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    file.bfSize = file.bfOffBits + width * height * 4;

    HANDLE output = CreateFileW(L"screen.bmp", GENERIC_WRITE, 0, nullptr,
                                CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (output == INVALID_HANDLE_VALUE) {
        std::fprintf(stderr, "CreateFile failed: %lu\n", GetLastError());
    } else {
        DWORD written = 0;
        WriteFile(output, &file, sizeof(file), &written, nullptr);
        WriteFile(output, &header, sizeof(header), &written, nullptr);
        void* pixels = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
        if (pixels && GetDIBits(memory, bitmap, 0, height, pixels,
                                reinterpret_cast<BITMAPINFO*>(&header), DIB_RGB_COLORS)) {
            WriteFile(output, pixels, width * height * 4, &written, nullptr);
        }
        if (pixels) HeapFree(GetProcessHeap(), 0, pixels);
        CloseHandle(output);
    }

    SelectObject(memory, old);
    DeleteObject(bitmap);
    DeleteDC(memory);
    ReleaseDC(nullptr, screen);
    return 0;
}

Compile with the Windows SDK, for example in a Visual Studio Developer Command Prompt:

cl /EHsc bitblt_capture.cpp user32.lib gdi32.lib

If BitBlt returns zero, preserve the logged error and verify:

  • The source and destination DC handles are non-null and still valid.
  • Both DCs refer to compatible devices. Microsoft documents failure when they represent different devices.
  • The source DC is not using a rotation or shear transformation, which BitBlt does not support.
  • The width and height are positive and within the source bounds.
  • You selected the compatible bitmap into the destination DC before calling BitBlt.

4. Protected windows can intentionally capture as black

Windows supports display-affinity protection for sensitive windows. Microsoft documents that SetWindowDisplayAffinity can cause protected content to appear black in captures; newer configurations can exclude a window from capture instead. This is a property of the target application, not a universal screenshot setting that a caller can override.

If ordinary windows capture correctly and one application remains black with a successful BitBlt call, ask the application owner whether it enables display protection. Banking, password, media and enterprise software commonly have reasons to restrict capture, but the symptom alone does not identify which mechanism is in use.

5. Compare BitBlt with Windows Graphics Capture

If your capture application offers both methods, capture the same target with each. Windows.Graphics.Capture has been available since Windows 10 version 1803. The exact menu and support depend on the application.

  • If Graphics Capture works and BitBlt is black, use Graphics Capture in that application and report the BitBlt result with your configuration.
  • If both methods fail only for one window, investigate protection or application-specific rendering.
  • If both fail for the entire desktop, continue with API logging and graphics diagnostics.

6. Check graphics drivers only when the wider system shows problems

A black screenshot alone is not proof of a bad driver. Look for device-manager errors, display corruption, flickering, resets or failures in other graphics applications. Microsoft’s guidance for code 43 describes it as a hardware or driver/software problem and recommends updating the device driver when that code is present. Reproduce the corruption under load and review hardware-acceleration settings as diagnostic steps.

Windows 10 support ended on October 14, 2025, so document the exact build when reporting a capture problem and follow the support path for your current Windows version.

7. Common errors and fixes

Symptom or error Cause to investigate Fix or next action
BitBlt returns zero Invalid DC, incompatible devices, unsupported transform or dimensions Log GetLastError; recreate compatible DCs and remove rotation/shear.
Return value is nonzero but image is black Protected target or capture-tool rendering path Test ordinary windows and compare Windows Graphics Capture.
Only a video, browser surface or accelerated app is black Special or protected rendering Try the application’s supported capture method; ask its vendor about capture restrictions.
Desktop and other apps show artifacts Driver, device or hardware fault Check Device Manager and vendor driver guidance, especially if code 43 appears.
OBS shows a black BitBlt source That OBS configuration’s selected capture path Compare OBS’s BitBlt and Windows Graphics Capture sources on the same window; community reports are configuration-specific.
Capture works on one monitor only Different adapters or incompatible DC devices Capture from a DC tied to the same device as the destination, or use a capture method designed for multi-monitor setups.

8. Performance and reliability notes

  • Capture only the rectangle you need; copying a full 4K desktop moves substantially more memory than a window-sized region.
  • Reuse compatible DCs and bitmaps for repeated captures, but recreate them after display-mode or device changes.
  • Check every Win32 return value and include the Windows build, monitor arrangement, scaling, GPU and capture method in logs.
  • Do not treat a black bitmap as a successful empty image. Store a verdict such as API failure, protected target or valid capture so downstream jobs can retry intelligently.
  • For unattended services, remember that a desktop session, permissions and window visibility can affect capture independently of BitBlt.

9. Or skip the browser setup

If your goal is a website screenshot rather than a Windows desktop or protected application, ScreenshotNeo provides a single HTTP request and an MCP server for AI agents. It removes cookie banners, newsletter popups and chat widgets before capture. 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.

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 also offers an MCP server with take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

10. FAQ

Does a black image prove BitBlt failed?

No. A successful call can still capture protected content as black. Check the return value and test another target.

Can I force a protected window to capture?

There is no universal caller-side override for a window that enables display-affinity protection. Use an allowed view or ask the application’s owner.

Should I replace my GPU?

Not based on this symptom alone. Investigate driver or hardware changes when the desktop shows wider corruption or Windows reports a device error.

Is Windows Graphics Capture always better?

No method is guaranteed for every target. Compare both methods in the application and configuration where the problem occurs.

Can ScreenshotNeo capture my Windows desktop?

ScreenshotNeo captures web URLs and PDFs. It is useful for website screenshots, not for protected local desktop windows.