在调用它恰好4,984次后,CreateCompatibleDC失败

时间:2014-01-13 19:46:33

标签: python winapi pywin32 win32gui

我的程序中遇到了一个奇怪的错误。这有点奇怪,因为它恰好发生在对函数的第4984次调用上。我一整天都在调整这一点,并且没有失败,这就是失败的数字。

有问题的代码是一个小的便利功能,它创建并返回DC和位图。这个小功能的背景是,它是我在屏幕录像机上的一个部分,所以它被称为吨和吨次。

当我第一次发现错误时,经过一些调查后,我找到this very similar Stackoverflow question,因此下面的代码是在该线程中的答案之后建模的。但是,即使遵循建议的删除和释放模式,问题仍然存在于第4984次迭代中。

这是该计划的具体失败点:

def _createDcAndBitmap(self, size, input_bitmap=None):
    hwnd = win32gui.GetDesktopWindow()
    zhwndDevice = win32gui.GetWindowDC(hwnd)
    zmfcDC  = win32ui.CreateDCFromHandle(zhwndDevice)
    zsaveDC = zmfcDC.CreateCompatibleDC()
    zsaveBitMap = win32ui.CreateBitmap()
    zsaveBitMap.CreateCompatibleBitmap(zmfcDC, *size)
    hOldBmp = zsaveDC.SelectObject(zsaveBitMap)
    return zsaveDC, zsaveBitMap, hOldBmp, hwnd

错误始终从该行开始:

zsaveBitMap.CreateCompatibleBitmap(zmfcDC, *size)  

Python报告的错误为:

error: CreateCompatibleDC failed  

FormatMessage拨打win32api可提供更多信息:

Invalid device context (DC) handle.  

完整代码:

class Bitmap(object):
    _sourceDC, _sourceBitmap, hOldBmp, hwnd = self._bytesToDcAndBitmap(bytestring, sourceSize)
    _bytes, _size = self._scaleBitmap(_sourceDC, _sourceBitmap, hOldBmp, hwnd,     sourceSize)


def _scaleBitmap(self, sourceDC, sourceBitmap, sourceHOldBmp, sourceHwnd, sourceSize):
    '''
    Resizes the current bitmap down to a target size 
    of (X, 540), where the X is varied depending on the 
    aspect ratio of the input bitmap 
    '''
    target_size = self._getTargetSize(sourceSize)
    destDC, destBitmap, hOldBmp, hwnd = self._createDcAndBitmap(target_size)

    win32gui.SetStretchBltMode(destDC.GetHandleAttrib(), 4)
    win32gui.StretchBlt(pywintypes.HANDLE(destDC.GetHandleAttrib()), 0,0,target_size[0], target_size[1],  # @UndefinedVariable HANDLE -- PyDev is dumb
                                                                                sourceDC.GetHandleAttrib(), 0,0, sourceSize[0], sourceSize[1], win32con.SRCCOPY)

    new_bytestring = destBitmap.GetBitmapBits(True)
    new_size = self._bitmapSize(destBitmap)

    self._deleteDCBitmapOldBmpAndHwmn(sourceDC, sourceBitmap, sourceHOldBmp, sourceHwnd)
    self._deleteDCBitmapOldBmpAndHwmn(destDC, destBitmap, hOldBmp, hwnd)


def _bytesToDcAndBitmap(self, bytestring, sourceSize):
    a = (ctypes.c_int * (sourceSize[0]*sourceSize[1]))()
    ctypes.memmove(a, bytestring, len(bytestring))

    hwnd = win32gui.GetDesktopWindow()
    zhwndDevice = win32gui.GetWindowDC(hwnd)
    zmfcDC  = win32ui.CreateDCFromHandle(zhwndDevice)
    zsaveDC = zmfcDC.CreateCompatibleDC()
    zsaveBitMap = win32ui.CreateBitmap()
    zsaveBitMap.CreateCompatibleBitmap(zmfcDC, sourceSize[0], sourceSize[1])
    hOldBmp = zsaveDC.SelectObject(zsaveBitMap)

    ctypes.windll.gdi32.SetBitmapBits(zsaveBitMap.GetHandle(), len(bytestring), ctypes.byref(a))

    return zsaveDC, zsaveBitMap, hOldBmp, hwnd  



def _createDcAndBitmap(self, size, input_bitmap=None):
    hwnd = win32gui.GetDesktopWindow()
    zhwndDevice = win32gui.GetWindowDC(hwnd)
    zmfcDC  = win32ui.CreateDCFromHandle(zhwndDevice)
    zsaveDC = zmfcDC.CreateCompatibleDC()
    zsaveBitMap = win32ui.CreateBitmap()
    zsaveBitMap.CreateCompatibleBitmap(zmfcDC, *size)
    hOldBmp = zsaveDC.SelectObject(zsaveBitMap)
    return zsaveDC, zsaveBitMap, hOldBmp, hwnd


def _deleteDCBitmapOldBmpAndHwmn(self, dc, bitmap, old_bitmap, hwnd):
    win32gui.SelectObject(dc.GetHandleAttrib(), old_bitmap.GetHandle())
    win32gui.DeleteDC(dc.GetHandleAttrib())
    win32gui.DeleteObject(bitmap.GetHandle())

    win32gui.ReleaseDC(win32gui.GetDesktopWindow(), hwnd)   

代码有点特殊,因为它在管道的“出口”端运行。所以它的工作是将序列化的byte string(从GetBitmapBits()获得)重建为位图,缩放它,然后返回byte string。这样做比使用更高级别的Python库快一个数量级。:)

所以,我猜这是由于某处存在内存泄漏,但据我所知,我正在关闭所有内容。然而,它仍然在第5000次呼叫时失败。

我在某处错过了泄漏吗?

0 个答案:

没有答案