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

Python循环中CreateCompatibleDC/DeleteDC失败,是否存在内存泄漏?

后台窗口截图循环报错解决方案

问题根因

你遇到的随机报错本质是GDI资源泄漏,Windows系统对单进程的GDI句柄数量有上限限制(默认10000个),当泄漏的句柄累积到上限后,DC、位图创建等操作就会随机失败。你当前的实现遗漏了以下关键注意点:

  • 资源释放顺序错误:saveDC是基于mfcDC创建的兼容DC,必须先释放saveDC再释放mfcDC,你当前的释放顺序相反,会导致mfcDC引用计数异常无法正常释放。
  • 未还原DC的原始选中对象:GDI规范要求,向DC中选入自定义位图后,操作完成必须选回DC默认的原始对象,否则被选中的位图无法被正常删除,直接造成资源泄漏。
  • 缺少前置有效性校验:每次循环调用FindWindow获取的窗口句柄可能为空(比如窗口被关闭、最小化、进程无响应、标题变动),未校验直接调用后续API会生成无效DC,释放逻辑也会失败。
  • 无异常兜底机制:循环中没有异常捕获逻辑,任意一步操作失败后,后续的资源释放代码不会执行,每次失败都会泄漏一批GDI句柄,快速累积到上限。
  • 未动态获取窗口尺寸:写死的screen_width、screen_height和窗口实际尺寸不符时,会导致位图创建、截图操作异常,间接引发资源泄漏。
  • 颜色转换参数错误:原代码中cv2.cvtColor使用了读取文件专用的IMREAD_ANYCOLOR参数,会导致图像处理异常,甚至触发内存泄漏。

修正后代码

import win32gui
import win32ui
from PIL import Image
import numpy as np
import cv2
from ctypes import windll

while True:
    target_window = win32gui.FindWindow(None, "Analytics dashboard - Google Chrome")
    # 校验窗口句柄有效性
    if not target_window:
        cv2.waitKey(100)
        continue
    
    hwndDC = None
    mfcDC = None
    saveDC = None
    saveBitMap = None
    old_obj = None
    try:
        # 动态获取窗口尺寸
        left, top, right, bottom = win32gui.GetWindowRect(target_window)
        screen_width = right - left
        screen_height = bottom - top

        hwndDC = win32gui.GetWindowDC(target_window) 
        mfcDC  = win32ui.CreateDCFromHandle(hwndDC)  
        saveDC = mfcDC.CreateCompatibleDC()
        
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(mfcDC, screen_width, screen_height)
        # 保存DC原来的选中对象
        old_obj = saveDC.SelectObject(saveBitMap)    
        
        # 校验PrintWindow调用结果
        result = windll.user32.PrintWindow(target_window, saveDC.GetSafeHdc(), 3)
        if not result:
            continue
            
        bmpinfo = saveBitMap.GetInfo()
        bmpstr = saveBitMap.GetBitmapBits(True)
        screen_image = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)        
        image = cv2.cvtColor(np.array(screen_image), cv2.COLOR_BGR2RGB)

        # 此处添加opencv窗口渲染逻辑
        # cv2.imshow("screen", image)
        # if cv2.waitKey(1) & 0xFF == ord('q'):
        #     break
        
    except Exception as e:
        print(f"截图出错: {str(e)}")
        cv2.waitKey(100)
    finally:
        # 按顺序释放资源,先还原DC选中对象
        if saveDC and old_obj:
            try:
                saveDC.SelectObject(old_obj)
            except:
                pass
        # 先释放子DC
        if saveDC:
            try:
                saveDC.DeleteDC()
            except:
                pass
        # 再释放父DC
        if mfcDC:
            try:
                mfcDC.DeleteDC()
            except:
                pass
        # 释放位图
        if saveBitMap:
            try:
                win32gui.DeleteObject(saveBitMap.GetHandle())
            except:
                pass
        # 释放窗口DC
        if target_window and hwndDC:
            try:
                win32gui.ReleaseDC(target_window, hwndDC)
            except:
                pass

cv2.destroyAllWindows()

内容的提问来源于stack exchange,提问作者D.Moore

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 23:45:03