在WPF应用程序中使用BitmapSource的OutOfMemory异常

时间:2017-03-16 09:25:09

标签: c# wpf bitmapsource writablebitmap

任务:我有两台显示器。我需要在#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(...)方法从第二个屏幕到第一个屏幕运行我的“反射”:

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。我在这里调查了一些帖子,但没有发现我的问题。

1 个答案:

答案 0 :(得分:3)

那是因为你在这里泄漏了内存:

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

它不应该再泄漏。