我的程序中遇到了一个奇怪的错误。这有点奇怪,因为它恰好发生在对函数的第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次呼叫时失败。
我在某处错过了泄漏吗?