Страницы

Поиск по вопросам

вторник, 31 декабря 2019 г.

Почему происходит утечка памяти?

#c_sharp #net #сервер #сокет #память


Есть метод который делает скриншот экрана каждые 20 мс и отправляет его клиенту.
Но после нескольких минут работы программа сжирает всю память, хотя вроде бы не должна.
Где происходит утечка памяти? 

private static async void Send(Socket handler)
    {
        await Task.Run(async () =>
        {
            try
            {
                while (true)
                {
                    ImageSource image = ScreenCapture.CopyScreen();
                    byte[] message = ImageSourceToBytes(new JpegBitmapEncoder(), image);
                    handler.Send(message);
                    await Task.Delay(20);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Сервер: " + ex.Message);
                handler.Shutdown(SocketShutdown.Both);
                handler.Close();
            }
        });

    }



  Методы работы с изображениями:


public static byte[] ImageSourceToBytes(BitmapEncoder encoder, ImageSource imageSource)
    {
        byte[] bytes = null;
        var bitmapSource = imageSource as BitmapSource;

        if (bitmapSource != null)
        {
            encoder.Frames.Add(BitmapFrame.Create(bitmapSource));

            using (var stream = new MemoryStream())
            {
                encoder.Save(stream);
                bytes = stream.ToArray();
            }
        }

        return bytes;
    }

 public static class ScreenCapture
{
    public static BitmapSource CopyScreen()
    {
        var left = Screen.AllScreens.Min(screen => screen.Bounds.X);
        var top = Screen.AllScreens.Min(screen => screen.Bounds.Y);
        var right = Screen.AllScreens.Max(screen => screen.Bounds.X + screen.Bounds.Width);
        var bottom = Screen.AllScreens.Max(screen => screen.Bounds.Y + screen.Bounds.Height);
        var width = right - left;
        var height = bottom - top;

        using (var screenBmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
        {
            using (var bmpGraphics = Graphics.FromImage(screenBmp))
            {
                bmpGraphics.CopyFromScreen(left, top, 0, 0, new System.Drawing.Size(width,
height));
                return Imaging.CreateBitmapSourceFromHBitmap(
                    screenBmp.GetHbitmap(),
                    IntPtr.Zero,
                    Int32Rect.Empty,
                    BitmapSizeOptions.FromEmptyOptions());
            }
        }
    }
}


UPDATE. Добавлен скриншот профайлера.

    


Ответы

Ответ 1



Проблема в том, что если вы вызываете функцию Bitmap.GetHbitmap, то вам необходимо вручную вызывать функцию DeleteObject из gdi32.dll для хэндлера, который вернула GetHbitmap. В противном случае действительно будет утечка. Вот немного модифицированный класс ScreenCapture: public class ScreenCapture : IDisposable { [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr hObject); private IntPtr hBitmap = IntPtr.Zero; private bool disposed; public BitmapSource CopyScreen() { if (disposed) throw new ObjectDisposedException(typeof(ScreenCapture).FullName); var left = Screen.AllScreens.Min(screen => screen.Bounds.X); var top = Screen.AllScreens.Min(screen => screen.Bounds.Y); var right = Screen.AllScreens.Max(screen => screen.Bounds.X + screen.Bounds.Width); var bottom = Screen.AllScreens.Max(screen => screen.Bounds.Y + screen.Bounds.Height); var width = right - left; var height = bottom - top; using (var screenBmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) { using (var bmpGraphics = Graphics.FromImage(screenBmp)) { bmpGraphics.CopyFromScreen(left, top, 0, 0, new System.Drawing.Size(width, height)); hBitmap = screenBmp.GetHbitmap(); return Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); } } } public void Dispose() { if (!disposed) { if (hBitmap != IntPtr.Zero) { DeleteObject(hBitmap); hBitmap = IntPtr.Zero; } disposed = true; } } } Код отправки в сокет: while (true) { byte[] message = null; using (var capture = new ScreenCapture()) { ImageSource image = capture.CopyScreen(); message = ImageSourceToBytes(new JpegBitmapEncoder(), image); } handler.Send(message); await Task.Delay(20); }

Комментариев нет:

Отправить комментарий