Woj*_*ech 9 python windows gdi+
问题
我正在使用 Windows 11 操作系统上的受阻窗口捕获屏幕截图windll.user32.PrintWindow(这又WM_PRINT根据文档进行调用)。一切都按预期工作,但是从 Windows 10 迁移到 Windows 11 后,性能非常不稳定。在 Win10 上,捕获通常需要不到 30 毫秒,在 Win11 上,有时接近这个时间,但有时屏幕捕获将持续数小时重复接近 300 毫秒(屏幕捕获在循环中运行)。在Win10上运行屏幕截图的几个月里从未出现过这种情况。造成这种缓慢性能的单行代码是对PrintWindow.
有趣的是,只有在捕获特定应用程序时才会出现性能缓慢的情况。这是一个第三方应用程序,我没有它的源代码,我只知道它使用Java。当尝试使用相同的屏幕捕获代码捕获其他应用程序时,性能符合预期 - 大约 20-30 毫秒。
附加信息
(WM_PRINT打印整个窗口和仅打印客户区域或)的时间大致相同WM_PRINTCLIENT。
时间与捕获窗口的大小呈线性关系。对于其他应用程序而言,情况并非如此 - PrintWindow 花费的时间大致相同(大约 30 毫秒),无论捕获的窗口是否占据全屏或尺寸是否大幅减小。
速度较慢的计算机(i5 9600 12 GB RAM)和较快的计算机(i7 10700 32 GB RAM)都会出现性能缓慢的情况。使用速度较慢的 PC 在 Win10 上运行屏幕捕获(捕获时间不到 30 毫秒)。
当性能变慢时,CPU 和 GPU 不会负担过重(查看任务管理器,它们的使用率不到 3%)。我没有注意到任何关于它何时变慢的模式。
动画的操作系统设置已关闭。另外,窗口没有最小化和恢复,所以据我了解,动画不应该是一个因素。
在捕获的应用程序中添加控件时,屏幕捕获会稍微变慢。然而,将控件减少到最低限度仍然无法让我接近所需的 30 毫秒捕获时间。
我最初的猜测:
操作系统对CPU/GPU要求过高。我认为在 i7 10700 上进行的测试与 i5 上的结果相同,证明情况并非如此。
应用程序的消息队列可能负载过重,并且我的 PrintWindow 调用正在排队等待。我认为性能随窗口大小线性缩放表明情况并非如此。我还尝试在调用 PrintWindow 之前调用 RedrawWindow - 没有效果。
可能的解决方案/解决方法:
在不牺牲所需信息的情况下使窗口尽可能小
同时捕获几个区域,然后将它们放在一起
使用 BitBlt 捕获桌面屏幕(窗口必须可见)
所有这些都没有解决核心问题 - 为什么这个特定窗口的绘制速度比所有其他窗口慢得多。任何想法都非常感激。
代码:代码是用Python编写的,但据我所知,PrintWindow的关键行是直接调用Windows dll。无论编程语言如何,请随意添加想法/解决方法。
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
Run Code Online (Sandbox Code Playgroud)
为了提高Java应用程序中PrintWindow的性能,您可以尝试使用以下代码:
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
Run Code Online (Sandbox Code Playgroud)
说明:PrintWindow函数是一个同步函数。当窗口不是最顶层时,窗口管理器可能需要等待其他窗口被绘制后才能绘制该窗口。这可能会导致 PrintWindow 函数变慢。当窗口位于最顶层时,窗口管理器不会等待其他窗口被绘制后才可以绘制该窗口。这可能会导致 PrintWindow 函数变快。
| 归档时间: |
|
| 查看次数: |
619 次 |
| 最近记录: |