基于Bitmap的应用截图异常:旧画面/黑屏问题求助
解决窗口截图旧画面/黑屏问题
核心原因
你遇到的问题是BitBlt的局限性导致的:BitBlt从屏幕显示缓冲区取数据,当窗口被遮挡、最小化,或是像Chrome这类启用硬件加速的应用(画面由GPU直接渲染,不经过系统屏幕DC),就只能捕获到缓存的旧画面甚至黑屏。RedrawWindow、UpdateWindow这类操作只会触发窗口重绘,但无法让BitBlt获取到非前台/被遮挡窗口的实时内容。
解决方案:改用PrintWindow
PrintWindow是Windows API专门用于捕获窗口内容的函数,它直接从窗口内部的绘图缓冲区获取数据,不受窗口是否在前台、是否被遮挡的影响,能拿到窗口的真实实时画面。
正确使用PrintWindow的步骤
- 导入依赖:通过Python自带的
ctypes调用user32.dll中的PrintWindow函数,无需额外安装库。 - 替换BitBlt调用:用PrintWindow替代原代码中的BitBlt,注意参数正确性。
修改后的截图函数
def screenshot(window_title): import ctypes user32 = ctypes.WinDLL('user32', use_last_error=True) hwnd = win32gui.FindWindow(None, window_title) if not hwnd: print("未找到指定标题的窗口") return None # 获取窗口尺寸 region = win32gui.GetWindowRect(hwnd) width = abs(region[0] - region[2]) height = abs(region[1] - region[3]) # 创建绘图上下文和位图 hwnd_dc = win32gui.GetWindowDC(hwnd) mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc) save_dc = mfc_dc.CreateCompatibleDC() save_bit_map = win32ui.CreateBitmap() save_bit_map.CreateCompatibleBitmap(mfc_dc, width, height) save_dc.SelectObject(save_bit_map) # 调用PrintWindow捕获整个窗口(参数1表示捕获整个窗口,0表示仅客户区) capture_success = user32.PrintWindow(hwnd, save_dc.GetSafeHdc(), 1) if not capture_success: print(f"PrintWindow调用失败,错误码: {ctypes.get_last_error()}") # 转换为PIL图像 bmp_info = save_bit_map.GetInfo() bmp_str = save_bit_map.GetBitmapBits(True) img = Image.frombuffer('RGB', (bmp_info['bmWidth'], bmp_info['bmHeight']), bmp_str, 'raw', 'BGRX', 0, 1) # 释放系统资源 win32gui.DeleteObject(save_bit_map.GetHandle()) save_dc.DeleteDC() mfc_dc.DeleteDC() win32gui.ReleaseDC(hwnd, hwnd_dc) return img.convert('RGB')
关键修改说明
- 移除了无效的
RedrawWindow和UpdateWindow调用,PrintWindow不需要这些前置操作 - 增加了窗口不存在的判断,避免后续报错
- 用
PrintWindow替代BitBlt,直接从窗口内部获取画面 - 加入了错误捕获,方便排查调用失败的问题
额外注意事项
- 窗口标题准确性:切换浏览器标签后,窗口标题会变化,要确保传入的
window_title是当前窗口的正确标题,否则FindWindow会匹配到旧窗口或找不到窗口 - 多显示器裁剪逻辑:检查
get_monitors()返回的坐标是否正确,确保裁剪用的box计算无误(可打印坐标值核对) - 捕获选项调整:如果某些应用捕获仍有问题,可以把PrintWindow的第三个参数改成
0(仅捕获窗口客户区),部分应用对整个窗口的捕获支持较差
内容的提问来源于stack exchange,提问作者Roland Deschain
相关产品推荐
相关产品推荐

