使用Windows API捕获Chrome窗口返回黑屏的原因求助
问题:捕获Chrome窗口截图返回黑屏,其他应用正常
我参考Stack Overflow上的Python代码写了一个捕获特定进程可见窗口截图的程序,大部分应用(比如DOS提示符)都能正常截图,但捕获Chrome窗口时只能得到黑屏。我尝试检查并捕获它的可见子窗口,还是没用。用的是Windows底层API,搞不懂哪里出问题了,求帮忙。
import win32gui import win32ui import win32con import time import sys from PIL import Image from PIL import ImageChops hw = [] bm = [] abc = 'abcdefghijklmnopqrstuvwxyz' pos = 0 target = sys.argv[1] print('target:',target) baby = 0 def screenshot(hwnd): print ( hex( hwnd ), win32gui.GetWindowText( hwnd ) ) l,t,r,b=win32gui.GetWindowRect(hwnd) h=b-t w=r-l hDC = win32gui.GetWindowDC(hwnd) myDC=win32ui.CreateDCFromHandle(hDC) newDC=myDC.CreateCompatibleDC() amap = win32ui.CreateBitmap() amap.CreateCompatibleBitmap(myDC, w, h) newDC.SelectObject(amap) win32gui.SetForegroundWindow(hwnd) time.sleep(0.05) newDC.BitBlt((0,0),(w, h) , myDC, (0,0), win32con.SRCCOPY) amap.Paint(newDC) amap.SaveBitmapFile(newDC, abc[pos] + '.bmp') return 1 def winEnumHandler( hwnd, ctx ): if win32gui.IsWindowVisible( hwnd ): if len( win32gui.GetWindowText( hwnd ) ) > 2: print ( hex( hwnd ), win32gui.GetWindowText( hwnd ) ) hw.append(hwnd) def winEnumHandlerStrict( hwnd, ctx ): if win32gui.IsWindowVisible( hwnd ): if target.lower() in win32gui.GetWindowText( hwnd ).lower() : print ( hex( hwnd ), win32gui.GetWindowText( hwnd ) ) hw.append(hwnd) l,t,r,b=win32gui.GetWindowRect(hwnd) print(str(l),str(t),str(r),str(b)) def enumBebe(hwnd, ctx ): if win32gui.IsWindowVisible( hwnd ): print('visible child', hwnd) baby = hwnd print ( hex( hwnd ), win32gui.GetWindowText( hwnd ) ) l,t,r,b=win32gui.GetWindowRect(hwnd) print(str(l),str(t),str(r),str(b)) else: print('invisible child', hwnd) win32gui.EnumWindows( winEnumHandlerStrict, None ) print('\n---\n',hw) win32gui.EnumChildWindows(hw[1],enumBebe,None) print(time.time()) for x in range(0,10): #pos = pos + screenshot(hw[1]) # command line is always first process to contain name of string we pass in pos = pos + screenshot(baby) print(time.time()) image_one = Image.open( abc[0] + '.bmp' ) image_two = Image.open( abc[6] + '.bmp' ) diff = ImageChops.difference(image_one, image_two) print( len(set(ImageChops.difference(image_one , image_two ).getdata())) )
问题原因
- 硬件加速冲突:Chrome(包括Edge、Electron类应用)默认启用GPU硬件加速,窗口内容由GPU直接渲染,普通
BitBlt无法捕获GPU绘制的层,只能得到黑屏。 - 子窗口句柄未正确获取:
enumBebe函数中的baby = hwnd是局部变量,外部全局baby始终为初始值0,等于没拿到有效子窗口句柄。 - 资源未释放:原代码未释放DC、位图等GDI资源,可能导致内存泄漏和异常。
修复方案
核心修改:用PrintWindow替代BitBlt
PrintWindow是Windows专门用于窗口截图的API,它会让窗口自身将内容绘制到指定DC,能绕过硬件加速限制。修改screenshot函数:
def screenshot(hwnd): global pos print(hex(hwnd), win32gui.GetWindowText(hwnd)) l, t, r, b = win32gui.GetWindowRect(hwnd) h = b - t w = r - l # 获取窗口DC并创建兼容DC hwnd_dc = win32gui.GetWindowDC(hwnd) mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc) save_dc = mfc_dc.CreateCompatibleDC() # 创建兼容位图 save_bitmap = win32ui.CreateBitmap() save_bitmap.CreateCompatibleBitmap(mfc_dc, w, h) save_dc.SelectObject(save_bitmap) # 激活窗口后用PrintWindow捕获内容 win32gui.SetForegroundWindow(hwnd) time.sleep(0.1) # 延长等待时间确保窗口激活 win32gui.PrintWindow(hwnd, save_dc.GetSafeHdc(), 0) # 0=捕获整个窗口,win32con.PW_CLIENTONLY=仅捕获客户区 # 保存位图并释放资源 save_bitmap.SaveBitmapFile(save_dc, abc[pos] + '.bmp') mfc_dc.DeleteDC() save_dc.DeleteDC() win32gui.ReleaseDC(hwnd, hwnd_dc) win32gui.DeleteObject(save_bitmap.GetHandle()) pos += 1 return 1
修复子窗口句柄获取问题
用列表存储子窗口句柄,避免局部变量覆盖问题:
# 全局变量改为列表存储子窗口 child_windows = [] def enumBebe(hwnd, ctx ): if win32gui.IsWindowVisible(hwnd): print('visible child', hwnd) child_windows.append(hwnd) print(hex(hwnd), win32gui.GetWindowText(hwnd)) l,t,r,b=win32gui.GetWindowRect(hwnd) print(str(l),str(t),str(r),str(b)) else: print('invisible child', hwnd) # 枚举子窗口后选择第一个可见子窗口(Chrome内容窗口通常是第一个) if hw: win32gui.EnumChildWindows(hw[0], enumBebe, None) baby = child_windows[0] if child_windows else hw[0]
完整修复代码
整合所有修改后的完整代码:
import win32gui import win32ui import win32con import time import sys from PIL import Image from PIL import ImageChops hw = [] abc = 'abcdefghijklmnopqrstuvwxyz' pos = 0 target = sys.argv[1] child_windows = [] baby = 0 print('target:', target) def screenshot(hwnd): global pos print(hex(hwnd), win32gui.GetWindowText(hwnd)) l, t, r, b = win32gui.GetWindowRect(hwnd) h = b - t w = r - l hwnd_dc = win32gui.GetWindowDC(hwnd) mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc) save_dc = mfc_dc.CreateCompatibleDC() save_bitmap = win32ui.CreateBitmap() save_bitmap.CreateCompatibleBitmap(mfc_dc, w, h) save_dc.SelectObject(save_bitmap) win32gui.SetForegroundWindow(hwnd) time.sleep(0.1) win32gui.PrintWindow(hwnd, save_dc.GetSafeHdc(), 0) save_bitmap.SaveBitmapFile(save_dc, abc[pos] + '.bmp') # 释放所有GDI资源 mfc_dc.DeleteDC() save_dc.DeleteDC() win32gui.ReleaseDC(hwnd, hwnd_dc) win32gui.DeleteObject(save_bitmap.GetHandle()) pos += 1 return 1 def winEnumHandlerStrict(hwnd, ctx ): if win32gui.IsWindowVisible(hwnd): window_text = win32gui.GetWindowText(hwnd) if target.lower() in window_text.lower(): print(hex(hwnd), window_text) hw.append(hwnd) l,t,r,b=win32gui.GetWindowRect(hwnd) print(str(l),str(t),str(r),str(b)) def enumBebe(hwnd, ctx ): if win32gui.IsWindowVisible(hwnd): print('visible child', hwnd) child_windows.append(hwnd) print(hex(hwnd), win32gui.GetWindowText(hwnd)) l,t,r,b=win32gui.GetWindowRect(hwnd) print(str(l),str(t),str(r),str(b)) else: print('invisible child', hwnd) win32gui.EnumWindows(winEnumHandlerStrict, None) print('\n---\n', hw) if hw: win32gui.EnumChildWindows(hw[0], enumBebe, None) baby = child_windows[0] if child_windows else hw[0] print(time.time()) for x in range(0,10): screenshot(baby) print(time.time()) image_one = Image.open(abc[0] + '.bmp') image_two = Image.open(abc[6] + '.bmp') diff = ImageChops.difference(image_one, image_two) print(len(set(diff.getdata())))
内容的提问来源于stack exchange,提问作者SlightlyKosumi
相关产品推荐
相关产品推荐

