使用设备上下文的内存泄漏问题

问题描述 投票:0回答:0

我正在使用 python 和 c++ 的组合来创建屏幕区域的快照,并将该屏幕截图用作视频源的一部分(标签 PyQt5 中的像素图),并将屏幕截图保存为 .bmp .. 目前完成以 30fps 的速度创建视频文件。到目前为止,代码在这个程度上工作得很好,除了我遇到的内存泄漏。

contextcreator.cpp,放入libccreator.so

#include "contextcreator.h"
#include <cstring>
#include <stdio.h>
#include <shlwapi.h>
#include <typeinfo>

BYTE* createContext(int x, int y, int width, int height){
    HDC hdesktop = GetDC(NULL);
    HDC memDC = CreateCompatibleDC(hdesktop);    
    HBITMAP hbitmap = CreateCompatibleBitmap(hdesktop, width, height);
    HGDIOBJ hbitmapOld = (HBITMAP)SelectObject(memDC, hbitmap);
    BitBlt(memDC, 0, 0, width, height, hdesktop, x, y, SRCCOPY|CAPTUREBLT);

    SelectObject(memDC, hbitmapOld);

    BITMAPINFO bmi = {0};
    bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);

    GetDIBits(hdesktop, hbitmap, 0, 0, NULL, &bmi, DIB_RGB_COLORS);

    BYTE* stream = new BYTE[bmi.bmiHeader.biSizeImage];
    bmi.bmiHeader.biCompression = BI_RGB;

    GetDIBits(hdesktop, hbitmap, 0, bmi.bmiHeader.biHeight, (LPVOID)stream, &bmi, DIB_RGB_COLORS);

    BYTE* data = new BYTE[14 + sizeof(bmi) + bmi.bmiHeader.biSizeImage];
    memcpy(data + 14, &bmi, sizeof(bmi));
    memcpy(&data[0] + sizeof(bmi) + 14, stream, bmi.bmiHeader.biSizeImage);

    for(int i = 0; i < 14; i++){
        data[i] = 0;  }

    delete[] stream;

    ReleaseDC(NULL, hdesktop);
    DeleteDC(memDC);

    return data;
}

void releaseData(BYTE* stream){
    delete[] stream;
}

使用 libccreator.so 的 python 代码

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from ctypes import *
import ctypes.wintypes as wintypes
import time
import os

os.add_dll_directory("C:/msys64/mingw64/bin")

mylib = cdll.LoadLibrary('C:/Users/amish_ac2c1jm/OneDrive/Documents/blahblah/libccreator.so')

create_context = mylib.createContext
create_context.argtypes = [c_int, c_int, c_int, c_int]
create_context.restype = POINTER(wintypes.BYTE)

release_stream = mylib.releaseData
release_stream.argtypes = [POINTER(wintypes.BYTE)]
release_stream.restype = None


class CaptureThread(QObject):
    finished = pyqtSignal()
    update_image = pyqtSignal([bytearray])

    def __init__(self, x, y, w, h, parent=None):
        super().__init__(parent)
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.stopthread = False
        self.framenumber = 0

    def run(self):
        test_timer = time.time()
        while not self.stopthread:
            if time.time() - test_timer >= 1000/30/1000:
                test_timer = time.time()
                self.capture()
        self.finished.emit()

    def capture(self):
        bmpptr = create_context(self.x, self.y, self.w, self.h)

        data = bytearray(string_at(addressof(bmpptr.contents) + 0x22, 0x4))
        size = int.from_bytes(data, byteorder='little', signed=False) + 0x36
        data = bytearray(string_at(bmpptr, size))

        release_stream(bmpptr)

        data[0:2] = b'BM'
        value = int.from_bytes(data[0x22:0x26], byteorder='little', signed=False)
        data[2:6] = (value + 0x36).to_bytes(4, byteorder='little', signed=False)
        data[6:10] = b'\x00\x00\x00\x00'
        data[10:14] = b'\x36\x00\x00\x00'

        with open(f"images/frame{self.framenumber}.bmp", "wb") as f:
            f.write(data)
        self.framenumber += 1

        self.update_image.emit(data)

最初我有一些内存泄漏,因为我没有删除用 new 关键字创建的字节数组,当我的显示器开始闪烁并且 chrome 和 pycharm 一起崩溃时,这种内存泄漏问题很快就很明显了。我最初也没有为 [硬件?] DC 使用 ReleaseDC,而是为该 DC 和内存 DC 使用 DeleteDC。我能够在任务管理器中直观地看到内存泄漏,因为我的项目很快就克服了 chrome 和 pycharm 的内存使用(这些 bmp 毕竟没有压缩......我稍后会研究)。尽管如此,内存泄漏仍然存在,但没有在任务管理器中显示我的应用程序,只显示我的整体内存使用量逐渐增加,直到我用完内存。大约需要 7 分钟左右(我有 16gb 的 RAM)。

我觉得跟DC有关系,但我不是很确定。我有一段时间使用 c++ 的经验,但是一旦我学习了 python,我就不会错过编译器和链接问题,至少可以说哈哈。 我将 PyCharm 用于我的 Python IDE,将 Qt Creator 用于 C++。在此先感谢您的帮助:)

python c++ winapi memory-leaks
© www.soinside.com 2019 - 2024. All rights reserved.