Есть метод который делает скриншот экрана каждые 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. Добавлен скриншот профайлера.
Ответ
Проблема в том, что если вы вызываете функцию 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);
}
Комментариев нет:
Отправить комментарий