【问题标题】:Memory leak with createDCFromHandle()/ createCompatibleDC()内存泄漏与 createDCFromHandle()/createCompatibleDC()
【发布时间】:2018-01-04 09:51:24
【问题描述】:

我正在尝试使用 GDI 捕获屏幕,我找到了一个代码 sn-p 并根据我的要求对其进行了修改。这是代码[EDIT:为简单起见修改了代码]:

def getfunc():
        # grab a handle to the main desktop window
        hdesktop = win32gui.GetDesktopWindow()

        # create a device context
        desktop_dc = win32gui.GetWindowDC(hdesktop)
        img_dc = win32ui.CreateDCFromHandle(desktop_dc)

        # create a memory based device context
        self.mem_dc = img_dc.CreateCompatibleDC()

        # create a bitmap object
        screenshot = win32ui.CreateBitmap()
        screenshot.CreateCompatibleBitmap(img_dc, width, height)
        self.mem_dc.SelectObject(screenshot)


        # copy the screen into our memory device context
        try:
            self.mem_dc.BitBlt((destUpLeftX, destUpLeftY), (width, height), img_dc, (srcUpLeftX, srcUpLeftY),win32con.SRCCOPY)
        except :
            logger.debug("BitBlt failed")

        img_dc.DeleteDC()
        win32gui.ReleaseDC(hdesktop, desktop_dc)

        win32gui.DeleteObject(screenshot.GetHandle())


def delete(self):
        self.mem_dc.DeleteDC()

现在,当我尝试运行此代码时,恰好经过 80 次迭代。我收到错误消息说 createDCFromHandle 失败或 createCompatibleDC 失败。

我查看以下stackoverflow question的任何解决方案

根据帖子,存在内存泄漏问题,我根据建议修改了删除功能。但我认为我仍然失踪,任何相同的指针?

【问题讨论】:

标签: python winapi memory-leaks gdi+ win32gui


【解决方案1】:

您似乎稍后会删除mem_dc。 screenshot(位图)在mem_dc 中仍处于选中状态,因此screenshot 的删除将延迟到mem_dc 的删除。这可能会遇到问题,具体取决于其余代码的设置方式。

您可以通过尽快从mem_dc 中选择位图来消除风险。以下是如何做到这一点的示例。

请注意,您有 10,000 个 GDI 句柄的限制,因此您的代码不应在 80 次迭代后失败。问题可能出在其他地方。

hwnd = win32gui.GetDesktopWindow()
hdc = win32gui.GetWindowDC(hwnd)
memdc = win32gui.CreateCompatibleDC(hdc)
hbitmap = win32gui.CreateCompatibleBitmap(hdc, 100, 100)
oldbmp = win32gui.SelectObject(memdc, hbitmap)

win32gui.BitBlt(memdc, 0, 0, 100, 100, hdc, 0, 0, win32con.SRCCOPY)

#use memdc here

win32gui.SelectObject(memdc, oldbmp)
win32gui.DeleteObject(hbitmap)
win32gui.ReleaseDC(hwnd, hdc)
win32gui.DeleteDC(memdc)


替代方法

使用bits = GetBitmapBits 并使用获取单个像素

p = (y * width + x) * 4
blue=bits[p+0]&0xFF
green=bits[p+1]&0xFF
red=bits[p+2]&0xFF

例子:

def foo(width, height):
    hwnd = win32gui.GetDesktopWindow()
    hdc = win32gui.GetWindowDC(hwnd)
    dc = win32ui.CreateDCFromHandle(hdc)
    memdc = dc.CreateCompatibleDC()
    bitmap = win32ui.CreateBitmap()
    bitmap.CreateCompatibleBitmap(dc, width, height)
    oldbmp = memdc.SelectObject(bitmap)
    memdc.BitBlt((0,0), (width,height), dc, (0,0), win32con.SRCCOPY)
    bits = bitmap.GetBitmapBits(False)
    memdc.SelectObject(oldbmp)
    win32gui.DeleteObject(bitmap.GetHandle())
    memdc.DeleteDC()
    win32gui.ReleaseDC(hwnd, hdc)
    return bits

width = 100
height = 100
bits = foo(width,height)

for y in range(0,10):
    for x in range(0,10):
        p = (y * width + x) * 4
        blu=bits[p+0]&0xFF
        grn=bits[p+1]&0xFF
        red=bits[p+2]&0xFF
        print("%02X%02X%02X " % (blu,grn,red), end='')
    print('')

【讨论】:

  • screenshot = win32guiui.CreateCompatibleBitmap(img_dc, width, height),它给了我错误“对象不是 pyhandle 对象”
  • 我的错,我在这里打错了.. screenshot = win32gui.CreateCompatibleBitmap(img_dc, width, height) 错误是 TypeError: The object is not a PyHANDLE object
  • 感谢代码,但我现在无法在 memdc 对象上调用 GetPixel(),因为我需要在几个位置读取像素值。
  • 那是因为这段代码会立即正确删除hbitmap,并且您可能稍后会调用win32gui.GetPixel,所以它会按预期失败。你应该在win32gui.SelectObject(memdc, oldbmp)之前调用win32gui.GetPixel。
  • 对了,你可以直接在桌面使用GetPixel,不需要内存dc。如果您需要所有像素,请使用screenshot.GetBitmapBits(False) 访问所有位。
猜你喜欢
  • 2011-10-16
  • 2019-08-08
  • 2012-08-21
  • 2016-01-25
  • 1970-01-01
  • 2021-11-24
  • 2015-04-15
  • 2014-03-26
  • 2012-07-09
相关资源
最近更新 更多