【问题标题】:CreateCompatibleDC() or DeleteDC() fail in continues loop in Python - possible memory leak?CreateCompatibleDC() 或 DeleteDC() 在 Python 的 continue 循环中失败 - 可能存在内存泄漏?
【发布时间】:2021-11-24 06:23:49
【问题描述】:

我正在使用下面这个特定的窗口屏幕捕获例程循环提供一个 opencv 窗口。

问题:经过数百次循环后,它突然在代码中标记的两个 FAIL POINTS 中的任何一个处失败。

我怀疑可能存在内存泄漏,但如果我没记错的话,我会删除并释放所需的内容以及我在删除之前(重新)选择对象它。

(我使用这种方法的原因,因为它即使特定窗口处于非活动状态并且在后台也能捕获它对我来说很重要而且我没有找到任何其他模块/method 确实有效。)

我忽略了什么?

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

while True:

        target_window = win32gui.FindWindow(None, ("Analytics dashboard - Google Chrome"))       
        
        hwndDC = win32gui.GetWindowDC(target_window) 
        mfcDC  = win32ui.CreateDCFromHandle(hwndDC)  

        saveDC = mfcDC.CreateCompatibleDC()  #### <-- RANDOM FAIL POINT 1: win32ui.error: CreateCompatibleDC failed
                
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(mfcDC, screen_width, screen_height)
        saveDC.SelectObject(saveBitMap)    
        result = windll.user32.PrintWindow(target_window, saveDC.GetSafeHdc(), 3)
        bmpinfo = saveBitMap.GetInfo()
        bmpstr = saveBitMap.GetBitmapBits(True)
        
        screen_image = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)        

        mfcDC.DeleteDC()  #### <-- RANDOM FAIL POINT 2: win32ui.error: DeleteDC failed

        saveDC.DeleteDC()               
        win32gui.DeleteObject(saveBitMap.GetHandle())        
        win32gui.ReleaseDC(target_window, hwndDC) 

        image = cv2.cvtColor(np.array(screen_image), cv2.IMREAD_ANYCOLOR)

【问题讨论】:

  • 确保target_window 不是0。进入任务管理器并监控“GDI使用情况”(默认不显示,您必须进入“详细信息”,然后选择GDI。看看GDI对象是否在增加。如果超过1000或2000个句柄就会有问题。
  • @Barmak Shemirani:感谢您的快速回复!检查,target_window 永远不会为 0,总是正确地得到它。,当它崩溃时也是如此。我还检查了 GDI 对象,并且确实增加了,因为您指示运行上面的代码。问题是为什么,因为在我看来,我正确地删除、释放和选择对象。请问您是否可以获取代码并在您身边进行测试以查看是否可以发现问题?
  • 我没有 windll 和 cv2 扩展名,我将它们注释掉并运行您的代码,看不到 GDI 泄漏。它不会超过 3。也许你在其他地方有 GDI 泄漏。
  • @Barmak Shemirani:再次感谢您的回答。根据您的评论,我也做了一些额外的测试,现在我可以确认,CreateCompatibleDC 或 DeleteDC 只有在添加 PrintWindow 行时才会失败。所以,不,我们可以将问题缩小到 printwindow。我试图在这里研究任何解决方案,但我找不到任何东西。请问您是否知道如何解决这个问题?

标签: python memory-leaks pywin32 win32gui win32-process


【解决方案1】:

我用ctypes.windll.user32.PrintWindow 尝试了上面的代码并且没有GDI 泄漏。 PrintWindow 的第三个参数应该是 PW_CLIENTONLY (1),或者有未记录的 PW_RENDERFULLCONTENT (2) 选项。未记录的代码不可靠。我不知道常量 (3) 指的是什么。

如果 Chrome 是顶部窗口,您应该只截取桌面的屏幕截图。这将是合规的。

如果你删除循环外的一些代码可能会有所帮助,至少它会更有效率。

import ctypes
import win32gui 
import win32ui
import win32con
from PIL import Image

hdesktop = win32gui.GetDesktopWindow()
(l, r, width, height) = win32gui.GetClientRect(hdesktop)
hdc = win32gui.GetWindowDC(hdesktop)
dc  = win32ui.CreateDCFromHandle(hdc)
memdc = dc.CreateCompatibleDC()
bitmap = win32ui.CreateBitmap()
bitmap.CreateCompatibleBitmap(dc, width, height)
memdc.SelectObject(bitmap)

while True:
    hwnd = win32gui.FindWindow("Chrome_WidgetWin_1", None)
    if hwnd == 0:
        break
    result = ctypes.windll.user32.PrintWindow(hwnd, memdc.GetSafeHdc(), 2)
    if result == 1:
        bytes = bitmap.GetBitmapBits(True)
        img = Image.frombuffer('RGB', (width, height), bytes, 'raw', 'BGRX', 0, 1)
        img.save("file.bmp")
    #break
    
dc.DeleteDC()
memdc.DeleteDC()
win32gui.DeleteObject(bitmap.GetHandle())
win32gui.ReleaseDC(hwnd, hdc)

您也可以在顶部添加ctypes.windll.shcore.SetProcessDpiAwareness(2)

【讨论】:

  • 非常感谢您抽出宝贵的时间。我完全理解您的建议,但不幸的是,这会导致黑屏。早些时候,我的代码也遇到了这个问题,这就是为什么我只使用目标窗口 hwnd,而不是先获取桌面,以及你提到的第三个常量必须是 3(虽然我不知道为什么)否则它总是由于某种原因出现黑屏。不过,我会尝试采纳您建议的更改,然后将结果回复您。
  • Chrome 有时会响应 PW_RENDERFULLCONTENT (2),有时则不会。我认为如果您输入3 或3000000,代码会将其更改为2。不同的程序可能会更改它0 或返回 false。没有处理未记录的常量的标准。
  • 花了一些时间,但我很高兴地确认,您提出的将大部分代码从循环中移出的建议非常有效!非常感谢您的时间和帮助!我接受了答案并投票了。
猜你喜欢
  • 1970-01-01
  • 2022-12-17
  • 2012-08-19
  • 2011-07-18
  • 1970-01-01
  • 1970-01-01
  • 2014-05-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多