There is a method that takes a screenshot of the screen every 20 ms and sends it to the client. But after a few minutes of operation, the program eats all the memory, although it seems it should not. Where does the memory leak occur?

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(); } }); } 

Methods of working with images:

 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. Added screenshot of profiler. enter image description here

  • profiler tried to watch? - Grundy
  • @Grundy, to be honest with no idea what it is. Is this some kind of VS tool? - Lightness
  • including VS too, look at this question - Grundy
  • @Grundy, Looked. The profiler shows the reason in the MemoryStream , added a screenshot. Although there shouldn't be any problems with it, as I use using / - Lightness

1 answer 1

The problem is that if you call the Bitmap.GetHbitmap function, then you need to manually call the DeleteObject function from gdi32.dll for the handler that returned GetHbitmap . Otherwise there will indeed be a leak.

Here is the slightly modified ScreenCapture class:

 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; } } } 

Code to send to socket:

 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); } 
  • The problem was solved, thanks! - Lightness