Issue
This Content is from Stack Overflow. Question asked by Wojciech
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 neededcapturing a couple of regions concurrently and then putting them
togetherCapturing 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
Solution
To improve the performance of PrintWindow in a Java application, You can try to use the following code:
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)
# Set the window to be topmost
win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
# Call PrintWindow
windll.user32.PrintWindow(hwnd, destDC.GetSafeHdc(), 2)
# Set the window back to normal
win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
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
Explanation: The PrintWindow function is a synchronous function. When the window is not topmost, the window manager may need to wait for other windows to be painted before it can paint the window. This may cause the PrintWindow function to be slow. When the window is topmost, the window manager will not wait for other windows to be painted before it can paint the window. This may cause the PrintWindow function to be fast.
This Question was asked in StackOverflow by Wojciech and Answered by Tibic4 It is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.