Коротко — два рабочих пути:
- "Простой" 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", ...).