Как в C++ сделать скриншот (захват изображения) второго монитора?

Я успешно делаю скриншот основного экрана с помощью <code class="cpp">HDC scrdc = GetDC(0);</code>, но не получается снять изображение любого другого монитора и также не могу получить единый снимок всех экранов (виртуального рабочего стола). Подскажите, пожалуйста, способы сделать скрин конкретного монитора или один большой скрин всех подключённых экранов.
  • 14 июля 2026 г. 7:00
Ответы на вопрос 1
Коротко — два рабочих пути:

- "Простой" GDI: взять DC рабочего стола (GetDC(NULL)), узнать координаты нужного монитора (EnumDisplayMonitors / GetMonitorInfo или GetSystemMetrics для виртуального экрана) и сделать BitBlt с нужного смещения. Это работает на всех версиях Windows, проще всего в реализации.
- Для более надёжного/быстрого захвата (особенно с аппаратными оверлеями и для игр) использовать DXGI Desktop Duplication API (Windows 8+).

Ниже пример на GDI, показывающий как снять конкретный монитор и как весь виртуальный рабочий стол.

Пример (Win32/C++) — захват конкретного монитора по HMONITOR и захват всего виртуального экрана:

```cpp
#include <windows.h>

HBITMAP CaptureMonitor(HMONITOR hMon)
{
    MONITORINFO mi = { sizeof(mi) };
    if (!GetMonitorInfo(hMon, &mi)) return NULL;

    int x = mi.rcMonitor.left;
    int y = mi.rcMonitor.top;
    int w = mi.rcMonitor.right - mi.rcMonitor.left;
    int h = mi.rcMonitor.bottom - mi.rcMonitor.top;

    HDC hScreenDC = GetDC(NULL);                     // DC для всего рабочего стола (виртуального)
    HDC hMemDC = CreateCompatibleDC(hScreenDC);
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, w, h);
    HGDIOBJ oldBmp = SelectObject(hMemDC, hBitmap);

    // CAPTUREBLT помогает захватить поверхностные/слоистые окна (layered)
    if (!BitBlt(hMemDC, 0, 0, w, h, hScreenDC, x, y, SRCCOPY | CAPTUREBLT)) {
        // ошибка
        SelectObject(hMemDC, oldBmp);
        DeleteObject(hBitmap);
        hBitmap = NULL;
    }

    SelectObject(hMemDC, oldBmp);
    DeleteDC(hMemDC);
    ReleaseDC(NULL, hScreenDC);

    return hBitmap; // caller освобождает DeleteObject(hBitmap)
}

// Пример использования: перечислить мониторы и сохранить HBITMAP для второго монитора
BOOL CALLBACK MonitorEnumProc(HMONITOR hMon, HDC, LPRECT, LPARAM lParam)
{
    std::vector<HMONITOR>* pVec = (std::vector<HMONITOR>*)lParam;
    pVec->push_back(hMon);
    return TRUE;
}

int main()
{
    // Собираем список мониторов
    std::vector<HMONITOR> mons;
    EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&mons);

    if (mons.size() >= 2) {
        HBITMAP bmp2 = CaptureMonitor(mons[1]); // второй монитор
        // сохранить bmp2 или обработать...
        if (bmp2) DeleteObject(bmp2);
    }

    // Захват всего виртуального рабочего стола:
    int vx = GetSystemMetrics(SM_XVIRTUALSCREEN);
    int vy = GetSystemMetrics(SM_YVIRTUALSCREEN);
    int vw = GetSystemMetrics(SM_CXVIRTUALSCREEN);
    int vh = GetSystemMetrics(SM_CYVIRTUALSCREEN);

    HDC hScreenDC = GetDC(NULL);
    HDC hMemDC = CreateCompatibleDC(hScreenDC);
    HBITMAP hBmpAll = CreateCompatibleBitmap(hScreenDC, vw, vh);
    HGDIOBJ oldBmp = SelectObject(hMemDC, hBmpAll);

    BitBlt(hMemDC, 0, 0, vw, vh, hScreenDC, vx, vy, SRCCOPY | CAPTUREBLT);

    // ... сохранить/обработать hBmpAll
    SelectObject(hMemDC, oldBmp);
    DeleteDC(hMemDC);
    ReleaseDC(NULL, hScreenDC);
    if (hBmpAll) DeleteObject(hBmpAll);

    return 0;
}
```

Замечания и советы
- GetDC(NULL) и GetDC(0) эквивалентны — это DC виртуального рабочего стола. Важно правильно задавать исходные координаты (для монитора не на позиции 0,0 координаты могут быть отрицательными).
- CAPTUREBLT в BitBlt помогает захватить поверхностные окна (layered). Тем не менее, некоторые аппаратные оверлеи/видео-особенности могут не захватываться GDI.
- Для высокопроизводительного и "правильного" захвата игр/видео используйте DXGI Desktop Duplication API (Windows 8+) — он даёт кадры напрямую от GPU и корректно захватывает оверлеи.
- В некоторых случаях можно использовать CreateDCA("DISPLAY", deviceName, NULL, NULL) с именем устройства из EnumDisplayDevices, но обычно проще брать DC рабочего стола и смещать src координаты по rcMonitor.
- DPI/scaling: если у вас Per-Monitor DPI, координаты мониторa через GetMonitorInfo даются в логических пикселях рабочего стола; обычно GDI-метод работает корректно, но учитывайте возможную масштабирование в специфичных сценариях.

Если нужно — могу дать:
- пример сохранения HBITMAP в файл .bmp,
- пример с использованием DXGI Desktop Duplication,
- или помочь получить deviceName через EnumDisplayDevices и показать CreateDC("DISPLAY", ...).
Похожие вопросы