PrintWindow и BitBlt скрытых окон
Моя программа делает скриншоты других окон приложений, чтобы автоматизировать некоторые задачи на них. Эти окна могут время от времени скрываться за кадром или скрываться другими окнами.
Чтобы уменьшить беспорядок, я удалил любую проверку ошибок из списков кода. Я готовлю оба типа скриншотов с
// Get size of the target window.
RECT clientRect;
GetClientRect(hwnd, &clientRect);
int width = clientRect.right - clientRect.left;
int height = clientRect.bottom - clientRect.top;
// Create a memory device context.
HDC windowDC = GetDC(hwnd);
HDC memoryDC = CreateCompatibleDC(windowDC);
// Create a bitmap for rendering.
BITMAPINFO bitmapInfo;
ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = width;
bitmapInfo.bmiHeader.biHeight = -height;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = width * height * 4;
RGBQUAD* buffer;
HBITMAP bitmap = CreateDIBSection(windowDC, &bitmapInfo, DIB_RGB_COLORS, (void**)&buffer, 0, 0);
HGDIOBJ previousObject = SelectOBject(memoryDC, bitmap);
тогда я делаю реальные скриншоты с
PrintWindow(hwnd, memoryDC, PW_CLIENTONLY);
GdiFlush();
или же
UpdateWindow(hwnd);
BitBlt(memoryDC, 0, 0, width, height, windowDC, 0, 0, SRCCOPY);
GdiFlush();
затем я копирую содержимое буфера в вектор
std::vector<RGBQUAD> pixels;
pixels.resize(width * height, { 0, 0, 0, 0 });
memcpy(&pixels[0], buffer, bitmapInfo.bmiHeader.biSizeImage);
и наконец я убираю все с
ReleaseDC(hwnd, windowDC);
SelectObject(memoryDC, previousObject);
DeleteDC(memoryDC);
DeleteObject(bitmap);
У меня есть три вопроса по поводу кода выше:
- Мне нужно позвонить
GdiFlush()
? Я прочитал документацию и провел некоторое исследование Google, но я все еще не уверен, имеет ли смысл называть это или нет. - Звонит ли
UpdateWindow()
прямо передBitBlt()
Сделать разницу? Помогает ли это, чтобы содержимое контекста устройства было "более современным"? - Прав ли я, если предположить, что для
PrintWindow()
вся работа выполняется из целевого приложения (увеличивается загрузка ЦП целевого приложения), аBitBlt()
полностью выполняется из вызывающего потока (таким образом увеличивая использование ЦП моего собственного приложения)? - При каких обстоятельствах может произойти сбой любой из перечисленных функций? я знаю это
BitBlt()
работает только для скрытых окон, если включена Композиция рабочего стола (DWM), но на очень небольшом наборе систем (Windows 8/10)BitBlt()
а такжеPrintWindow()
похоже, не работают для окон, для которых они отлично работают в других системах (даже если DWM включен). Хотя я не мог определить, почему.
Любая информация приветствуется, спасибо.
1 ответ
Спустя более двух месяцев я наконец понял, что это происходит только с обновлением Windows 1809 от конца 2018. Очевидно, Windows изменила способ обработки клиппирования с этим обновлением, так что содержимое контекста устройства больше не обновляется для частей окон, которые расположены закадровый.
Чтобы ответить на отдельные вопросы:
1) GdiFlush, похоже, не имеет значения, по крайней мере, из того, что я могу сказать до сих пор.
2) Все еще не уверен на 100%, но я также думаю, что это не имеет значения.
3) До сих пор не знаю.
4) См ответ выше.
Наконец , после нескольких часов исследования я нашел решение для этой проблемы: достаточно вызвать следующую команду в событии ACTIVATE формы, которую нужно отобразить (пример в VB-кодировании):
Call SetWindowLong(me.hwnd, GWL_EXSTYLE, WS_EX_LAYERED)
Принимая во внимание, что эта команда определена следующим образом:
Private Declare Function SetWindowLong Lib "User32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Const GWL_EXSTYLE = (-20)
Private Const WS_EX_LAYERED = &H80000
Пожалуйста, попробуйте это!
С наилучшими пожеланиями, Bambi66