0

Task: I got 2 monitors. And I need to show on #1 what is going on #2. In another words, first monitor is nothing but a reflector of second.

Current solution: Just making screenshot every ~100ms and re-render. Following method is responsible for capturing screenshots:

private BitmapSource MakeScreenshot(Screen screen)
    {
        using (var screenBmp = new Bitmap(screen.Bounds.Width, screen.Bounds.Height, PixelFormat.Format32bppArgb))
        {
            using (var bmpGraphics = Graphics.FromImage(screenBmp))
            {
                bmpGraphics.CopyFromScreen(screen.Bounds.X, screen.Bounds.Y, 0, 0, screen.Bounds.Size);

                return 
                    Imaging.CreateBitmapSourceFromHBitmap(
                        screenBmp.GetHbitmap(),
                        IntPtr.Zero,
                        Int32Rect.Empty,
                        BitmapSizeOptions.FromEmptyOptions());
            }
        }
    }

After that I uset Start(...) method to run my "reflection" from second screen to first:

public void Start(int delay, int period)
    {
        if (_timer != null) throw new InvalidOperationException();

        _timer = new System.Threading.Timer(
            _ =>
            {
                _placeholder
                    .Dispatcher
                    .Invoke(() =>
                    {
                        _placeholder.Source = MakeScreenshot(_targetScreen); // re-render new screenshot
                    });
            }, 
            null, 
            delay, 
            period);
    }

Problem: After around 30-40 second of pretty nice run it fails with OutOfMemoryException. I've investigated some of posts here, but found nothing regarding my problem.

2
  • Do you clean up the Screenshots after you dont need them anymore? Commented Mar 16, 2017 at 9:31
  • @lokusking First: I use Using(...) for IDisposable. BitmapSource doesn't implement IDisposable and doesn't have something like .Clean() method. Commented Mar 16, 2017 at 9:32

1 Answer 1

3

That is because you leak memory here:

Imaging.CreateBitmapSourceFromHBitmap(
    screenBmp.GetHbitmap(), // < here
    IntPtr.Zero,
    Int32Rect.Empty,
    BitmapSizeOptions.FromEmptyOptions());

You need to free memory used by GDI bitmap after you call screenBmp.GetHbitmap(). Change that like this:

private BitmapSource MakeScreenshot(Screen screen)
{
    using (var screenBmp = new Bitmap(screen.Bounds.Width, screen.Bounds.Height, PixelFormat.Format32bppArgb))
    {
        using (var bmpGraphics = Graphics.FromImage(screenBmp))
        {
            bmpGraphics.CopyFromScreen(screen.Bounds.X, screen.Bounds.Y, 0, 0, screen.Bounds.Size);
            var handle = screenBmp.GetHbitmap();
            try {
                return
                    Imaging.CreateBitmapSourceFromHBitmap(
                        handle,
                        IntPtr.Zero,
                        Int32Rect.Empty,
                        BitmapSizeOptions.FromEmptyOptions());
            }
            finally {
                DeleteObject(handle);
            }
        }
    }
}

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

And it should not leak any more.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.