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

huangapple go评论72阅读模式
英文:

Memory leak issue with using device contexts

问题

以下是您要翻译的代码部分:

#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;
}
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)
英文:

I'm using a combination of python and c++ to create a snapshot of a screen area and use that screenshot as part of a video feed (pixmap in a label PyQt5) as well as save the screenshot as a .bmp.. currently done at 30fps for creating a video file later. The code works to this degree so far just fine, aside from the memory leak I'm getting.

contextcreator.cpp, put into libccreator.so

#include &quot;contextcreator.h&quot;
#include &lt;cstring&gt;
#include &lt;stdio.h&gt;
#include &lt;shlwapi.h&gt;
#include &lt;typeinfo&gt;
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, &amp;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, &amp;bmi, DIB_RGB_COLORS);
BYTE* data = new BYTE[14 + sizeof(bmi) + bmi.bmiHeader.biSizeImage];
memcpy(data + 14, &amp;bmi, sizeof(bmi));
memcpy(&amp;data[0] + sizeof(bmi) + 14, stream, bmi.bmiHeader.biSizeImage);
for(int i = 0; i &lt; 14; i++){
data[i] = 0;  }
delete[] stream;
ReleaseDC(NULL, hdesktop);
DeleteDC(memDC);
return data;
}
void releaseData(BYTE* stream){
delete[] stream;
}

The python code that utilizes libccreator.so

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(&quot;C:/msys64/mingw64/bin&quot;)
mylib = cdll.LoadLibrary(&#39;C:/Users/amish_ac2c1jm/OneDrive/Documents/blahblah/libccreator.so&#39;)
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 &gt;= 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=&#39;little&#39;, signed=False) + 0x36
data = bytearray(string_at(bmpptr, size))
release_stream(bmpptr)
data[0:2] = b&#39;BM&#39;
value = int.from_bytes(data[0x22:0x26], byteorder=&#39;little&#39;, signed=False)
data[2:6] = (value + 0x36).to_bytes(4, byteorder=&#39;little&#39;, signed=False)
data[6:10] = b&#39;\x00\x00\x00\x00&#39;
data[10:14] = b&#39;\x36\x00\x00\x00&#39;
with open(f&quot;images/frame{self.framenumber}.bmp&quot;, &quot;wb&quot;) as f:
f.write(data)
self.framenumber += 1
self.update_image.emit(data)

Originally i had some memory leaks from not deleting the byte arrays created with the new keyword, and such memory leak issue was apparent quite quickly when my monitors started blinking and chrome would crash along with pycharm. I also wasn't originally using ReleaseDC for the [hardware?] DC, but instead using DeleteDC for both that DC and the memory DC. I was able to visually see the memory leak in task manager as my project very quickly overcame chrome and pycharm memory usage (these bmp's aren't compressed after all.. something I'll look into later). Still, a memory leak persists but doesn't show up for my app in task manager, only showing my overall memory usage gradually increase until i run out of memory. Takes about 7min or so (i have 16gb of RAM).

I feel it has something to do with the DC's, but i'm not entirely sure. I have some experience with c++ from a while back, but once i learned python i didn't miss compiler and linking issues to say the least lol.
I use PyCharm for my python IDE and Qt Creator for c++. Thanks in advance for any help 使用设备上下文时的内存泄漏问题

答案1

得分: 1

根据Igor Tandetnik的评论回复,泄漏是因为没有删除从CreateCompatibleBitmap函数返回的对象。

英文:

As per the comment response from Igor Tandetnik, the leak was from not deleting the object returned from the CreateCompatibleBitmap function.

huangapple
  • 本文由 发表于 2023年2月19日 22:08:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/75500698.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定