Reputation: 4119
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.
Upvotes: 0
Views: 475
Reputation: 101463
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.
Upvotes: 3