WPF 应用程序中 BitmapSource 的 OutOfMemory 异常
OutOfMemory exception with BitmapSource in WPF app
任务: 我有 2 个显示器。我需要在#1 上展示#2 发生了什么。换句话说,第一台显示器不过是第二台显示器的反射器。
当前解决方案: 每隔约 100 毫秒制作一次屏幕截图并重新渲染。
以下方法负责截图:
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());
}
}
}
之后,我使用 Start(...) 方法 运行 我的 "reflection" 从第二个屏幕到第一个屏幕:
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);
}
问题: 大约 30-40 秒后 运行 它因 OutOfMemoryException 而失败。我调查了这里的一些帖子,但没有发现任何关于我的问题的信息。
那是因为你这里内存泄漏:
Imaging.CreateBitmapSourceFromHBitmap(
screenBmp.GetHbitmap(), // < here
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
调用screenBmp.GetHbitmap()
后需要释放GDI位图使用的内存。像这样更改:
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);
应该不会再漏了。
任务: 我有 2 个显示器。我需要在#1 上展示#2 发生了什么。换句话说,第一台显示器不过是第二台显示器的反射器。
当前解决方案: 每隔约 100 毫秒制作一次屏幕截图并重新渲染。 以下方法负责截图:
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());
}
}
}
之后,我使用 Start(...) 方法 运行 我的 "reflection" 从第二个屏幕到第一个屏幕:
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);
}
问题: 大约 30-40 秒后 运行 它因 OutOfMemoryException 而失败。我调查了这里的一些帖子,但没有发现任何关于我的问题的信息。
那是因为你这里内存泄漏:
Imaging.CreateBitmapSourceFromHBitmap(
screenBmp.GetHbitmap(), // < here
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
调用screenBmp.GetHbitmap()
后需要释放GDI位图使用的内存。像这样更改:
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);
应该不会再漏了。