Утечка памяти с помощью createDCFromHandle()/ createCompatibleDC()

Я пытаюсь захватить экран с помощью GDI, я нашел один фрагмент кода и изменил его в соответствии с моим требованием. Вот код[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.

Я просматриваю любое решение, которое пришло к следующему вопросу о переполнении стека: /а>

Согласно сообщению, существует проблема утечки памяти, я изменил свою функцию удаления в соответствии с предложением. Но я думаю, что мне все еще не хватает, какие-либо указатели на то же самое?


person Rahul    schedule 04.01.2018    source источник
comment
Покажите минимально воспроизводимый пример. Вы наверняка сливаете все, что создаете в getfunc и храните в локальных переменных.   -  person David Heffernan    schedule 04.01.2018
comment
@DavidHeffernan попытался удалить локальные переменные, но все та же проблема, есть ли другие указатели?   -  person Rahul    schedule 05.01.2018
comment
минимальный воспроизводимый пример пожалуйста...   -  person David Heffernan    schedule 05.01.2018


Ответы (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('')
person Barmak Shemirani    schedule 04.01.2018
comment
скриншот = win32guiui.CreateCompatibleBitmap (img_dc, ширина, высота), это дало мне ошибку, объект не является объектом pyhandle - person Rahul; 05.01.2018
comment
мой плохой, я сделал опечатку здесь .. screenshot = win32gui.CreateCompatibleBitmap(img_dc, ширина, высота) и ошибка TypeError: объект не является объектом PyHANDLE - person Rahul; 05.01.2018
comment
спасибо за код, но сейчас я не могу вызвать GetPixel() для объекта memdc, так как мне нужно прочитать значение пикселя в нескольких местах. - person Rahul; 05.01.2018
comment
Это потому, что этот код правильно удаляет hbitmap сразу, и вы, вероятно, вызываете win32gui.GetPixel позже, поэтому он не работает, как и ожидалось. Вы должны позвонить win32gui.GetPixel до win32gui.SelectObject(memdc, oldbmp). - person Barmak Shemirani; 05.01.2018
comment
Кстати, вы можете использовать GetPixel прямо на рабочем столе, без необходимости в постоянной памяти. Если вам нужны все пиксели, используйте screenshot.GetBitmapBits(False) для доступа ко всем битам. - person Barmak Shemirani; 06.01.2018
comment
Спасибо за указатель... но как нам получить доступ к одному местоположению в этом массиве пикселей, если у нас есть растровое изображение? - person Rahul; 08.01.2018
comment
Возвращаемые биты представляют собой значения RGBQUAD, используйте p = (y * width + x) * 4 для поиска пикселей. - person Barmak Shemirani; 08.01.2018