The problem
I am capturing a screenshot from an obstructed window on Windows 11 OS using windll.user32.PrintWindow
(which in turn is calling WM_PRINT
according to the docs).
Everything works as expected however after moving from Windows 10 to Windows 11 the performance has been very unstable.
On Win10 it usually took under 30ms for the capture, on Win11 it's sometimes close to that but every now and then the screencapture will take close to 300ms repeatedly for hours (the screencapture is running in a loop). This has never occured during many months of the
screen capturing running on Win10. The single line of code responsible for this slow performance is the call to PrintWindow
.
The interesting part is that the slow performance occurs only when capturing a particular application. It's a third party application and I don't have its source code, I only know it is using Java. When trying to capture other applications using the same code for screen capture, the performance is in line with expectations - around 20-30 ms.
Additional info
The time is roughly the same for both printing the whole window and only the clientarea
(WM_PRINT
orWM_PRINTCLIENT
).The time is scaling pretty linearly with the size of the captured window. This is NOT the case for other applications - PrintWindow takes roughly the same (around 30ms) regardless if the captured window takes up the full screen or the size is very strongly reduced.
The slow performance happens on both a slower machine (i5 9600 12 GB RAM) as well as on a quicker one (i7 10700 32 GB RAM). The slower PC was used to run the screencapture on Win10 (capturing in under 30ms).
The CPU and GPU are not overburdened when the performance get slower (looking at task manager they are using less than 3%). I didn't notice any pattern as to when does it become slower.
The OS settings for animations are switched off. Also, The window is not minimized and restored, so as far as I understand, the animations should not be a factor.
The screencapture becomes slightly slower when adding controls in the captured app. However, reducing the controls to a bare minimum still doesn't get me close to the desired 30ms capture time.
My guesses initially:
OS overly demanding for CPU/GPU. I think testing on i7 10700 with the same results as on i5 proves otherwise.
Application's messagequeue might be heavily loaded and my PrintWindow call is waiting in line. I would assume the performance scaling linearly with the window size suggests otherwise. I also tried calling RedrawWindow before calling PrintWindow - no effect.
Possible solutions/workarounds:
make the window as small as possible without sacrificing the information needed
capturing a couple of regions concurrently and then putting them together
Capturing the desktopscreen using BitBlt (the window has to be visible)
All of these don't address the core problem - why this particular window draws much slower than all the other windows. Any ideas much appreciated.
The code: the code is in python however the one crucial line with PrintWindow is as far as I know calling Windows dll directly. Please feel free to add ideas/workarounds regardless of the programming language.
def capture_screen_from_DC(hwnd):
l, t, r, b = win32gui.GetWindowRect(hwnd)
w = r - l
h = b - t
hwndDC = win32gui.GetWindowDC(hwnd)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
destDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
destDC.SelectObject(saveBitMap)
windll.user32.PrintWindow(hwnd, destDC.GetSafeHdc(), 2)
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
im = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
win32gui.DeleteObject(saveBitMap.GetHandle())
destDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
return im