Aparentemente, Microsoft ha cambiado la forma en que funciona el recorte con la actualización de Windows 1809, lanzada a fines de 2018. Antes de esa actualización, GetClipBox()
devolvió el rectángulo de cliente completo de una ventana, incluso cuando estaba (parcialmente) fuera de la pantalla. Después de la actualización, la misma función devuelve un rectángulo recortado, que solo contiene las partes que todavía están en pantalla. Esto hace que el contenido del contexto del dispositivo no se actualice para el área fuera de la pantalla, lo que me impide tomar capturas de pantalla de estas ventanas.
La pregunta es: ¿puedo manipular de alguna manera la región de recorte?
Investigué un poco y parece que la región de recorte final está influenciada por la región de la ventana, el rectángulo de actualización y la región del sistema, según tengo entendido, la “región de recorte global”. He comprobado la región de la ventana con GetWindowRgn()
y GetRgnBox()
ambos devuelven los mismos valores para Windows 1809 y versiones anteriores. GetUpdateRect()
también devuelve el rectángulo completo del cliente, por lo que ese tampoco puede ser el problema. También he intentado enganchar el BeginPaint()
método y ver si cambiar el PAINTSTRUCT.rcPaint
hace cualquier cosa, sin éxito.
Entonces, lo que me queda es tratar de ajustar la región del sistema, o a veces llamada la región visible. Sin embargo, no tengo ni idea de si y cómo eso es posible. MSDN sugiere que no espero pensé que tal vez alguien tiene una idea para una solución!?
EDITAR: Para que esto quede más claro, no creo que la aplicación misma realice el recorte, porque las capturas de pantalla fuera de pantalla de la misma versión de la aplicación funcionan antes de Windows 1809 y no funcionan con la versión actualizada de Windows. En cambio, el propio Windows parece recortar cualquier superficie fuera de la pantalla.
EDIT2: Aquí hay un ejemplo de código de trabajo mínimo para tomar la captura de pantalla.
// Get the client size.
RECT crect;
GetClientRect(hwnd, &crect);
int width = crect.right - crect.left;
int height = crect.bottom - crect.top;
// Create DC and Bitmap.
HDC windowDC = GetDC(hwnd);
HDC memoryDC = CreateCompatibleDC(windowDC);
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;
char* pixels;
HBITMAP bitmap = CreateDIBSection(windowDC, &bitmapInfo, DIB_RGB_COLORS, (void**)&pixels, 0, 0);
HGDIOBJ previousObject = SelectObject(memoryDC, bitmap);
// Take the screenshot. Neither BitBlt nor PrintWindow work.
BitBlt(memoryDC, 0, 0, width, height, windowDC, 0, 0, SRCCOPY);
// ..or..
// PrintWindow(hwnd, memoryDC, PW_CLIENTONLY);
// Save the image.
BITMAPFILEHEADER bitmapFileHeader;
bitmapFileHeader.bfType = 0x4D42;
bitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
std::fstream hFile("./screenshot.bmp", std::ios::out | std::ios::binary);
if(hFile.is_open())
{
hFile.write((char*)&bitmapFileHeader, sizeof(bitmapFileHeader));
hFile.write((char*)&bitmapInfo.bmiHeader, sizeof(bitmapInfo.bmiHeader));
hFile.write(pixels, (((32 * width + 31) & ~31) / 8) * height);
hFile.close();
}
// Free Resources
ReleaseDC(hwnd, windowDC);
SelectObject(memoryDC, previousObject);
DeleteDC(memoryDC);
DeleteObject(bitmap);
Puedes descargar un ejecutable compilado de Google Drive aquí. el uso es Screenshot.exe <HWND>
, donde HWND es la dirección hexadecimal del identificador de la ventana, como se muestra en Spy++, por ejemplo. Guardará una captura de pantalla de la ventana de destino en el directorio de trabajo como screenshot.bmp
(asegúrese de que tiene permiso para escribir en el directorio). La captura de pantalla funcionará para casi todas las ventanas (incluso si están ocultas detrás de otras ventanas), pero tan pronto como mueva parcialmente la ventana fuera de la pantalla, la captura de pantalla continuará mostrando el contenido de la ventana anterior para la parte fuera de la pantalla de la ventana (cámbiela de tamaño). mientras está fuera de la pantalla, por ejemplo, para ver el efecto). Esto solo sucede en Windows 1809, aún muestra contenidos actualizados en versiones anteriores de Windows.
EDIT3: Investigué un poco más sobre esto. Con respecto a la aplicación AdobeAir para la cual el WS_EX_LAYERED
style no funcionó: descubrí que usa BitBlt
internamente renderice el búfer posterior en la ventana dc. Los pasos de renderizado son:
GetDC(hwnd)
en la ventana para obtenerhdcWin
CreateCompatibleDC(hdcWin)
para crear unhdcMem
- Llamar
SelectObject(hdcMem, bmp)
para seleccionar unHBITMAP
dentrohdcMem
BitBlt
dehdcMem
ahdcWin
. Durante elBitBlt
llama ahdcMem
contiene datos de píxeles válidos incluso en las regiones fuera de la pantalla, pero esos datos nunca se copian en elhdcWin
.
Observé las regiones del sistema durante el BitBlt
llamar. Para hdcMem
la región del sistema es un NULLREGION
pero para el hdcWin
la región siempre se recorta en los bordes de la pantalla. También traté de ajustar la región del sistema reemplazando todas las llamadas a GetDC
con GetDCEx(hwnd, hrgn, DCX_CACHE | DCX_INTERSECTRGN)
(como se mencionó en este articulo), pero eso no funciona y no parece brindar opciones para extender la región. Realmente creo que el secreto para resolver el problema radica en manipular la región del sistema para la ventana dc, pero no tengo idea de cómo hacerlo.
Si se encuentra que el CreateDC
función toma un puntero a un DEVMODE
struct como último argumento (MSDN). Que a su vez tiene campos dmPelsWidth
, dmPelsHeight
y dmPosition
. Creo que estos conforman la región del sistema y tal vez si pudiera manipularlos, el DC ya no se recortaría, pero no pude enganchar el CreateDC
función, todavía.
Si tiene nuevas ideas basadas en mis nuevos conocimientos, compártalas. ¡Agradecería cualquier ayuda!
Según tengo entendido, desea cambiar la región de recorte de otro programas? Voy a canalizar a Raymond Chen aquí y preguntar: ¿qué pasaría si dos programas intentaran hacer eso simultáneamente? El sistema operativo puede hacer eso porque, por definición, solo hay uno.
– MSalters
7 febrero 2019 a las 15:16
Puedo reproducir (también usando la muestra oficial docs.microsoft.com/en-us/windows/desktop/gdi/capturing-an-image) y, de hecho, ni siquiera necesito codificar nada. Inicie Windows (el mío es Windows 10, 64 bits, 6.3.17763), abra el Bloc de notas, muévalo hasta la mitad de la pantalla y pegue una gran parte del texto en él. Ejecute ALT-TAB para mostrar miniaturas centradas (o mueva el mouse a la barra de tareas) y verá que solo la mitad del bloc de notas está pintada con texto… huele a error.
– Simón Moulier
19 de febrero de 2019 a las 19:08
En mi humilde opinión, debe informarlo a Microsoft, ya que incluso su código de muestra demuestra el problema.
– Simón Moulier
20 de febrero de 2019 a las 9:29
Creé un informe de error para esto en el Centro de comentarios de Windows. Aquí está el enlace alias.ms/AA4c5yc
-Johannes Stricker
21 de febrero de 2019 a las 9:40
Desafortunadamente, el problema existe también en Windows 1903 y 1909.
– María
16 de noviembre de 2019 a las 17:31