You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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())) )

问题原因

  1. 硬件加速冲突:Chrome(包括Edge、Electron类应用)默认启用GPU硬件加速,窗口内容由GPU直接渲染,普通BitBlt无法捕获GPU绘制的层,只能得到黑屏。
  2. 子窗口句柄未正确获取:enumBebe函数中的baby = hwnd是局部变量,外部全局baby始终为初始值0,等于没拿到有效子窗口句柄。
  3. 资源未释放:原代码未释放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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 13:03:09