【问题标题】:Bitmap Graphics CreateBitmapSourceFromHBitmap memory leak位图图形 CreateBitmapSourceFromHBitmap 内存泄漏
【发布时间】:2015-10-14 22:02:59
【问题描述】:

我想在带有 WPF 的 Image 容器中显示 Bitmap

private void updateImagePreview()
{
    Bitmap bmp = new Bitmap(Screen.PrimaryScreen.WorkingArea.Width,
                            Screen.PrimaryScreen.WorkingArea.Height);
    Graphics gr = Graphics.FromImage(bmp);
    Image Image_Preview;

    while (true)
    {
        gr.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(bmp.Width, bmp.Height));
        Image_Preview.Source = loadBitmap(bmp);
    }
}

[DllImport("gdi32")]
static extern int DeleteObject(IntPtr o);
public BitmapSource loadBitmap(System.Drawing.Bitmap source)
{
    IntPtr ip = source.GetHbitmap();
    BitmapSource bs = null;
    try
    {
        bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                 ip, IntPtr.Zero, Int32Rect.Empty,
                 System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
    }
    finally
    {
        DeleteObject(ip);
    }

    return bs;
}

问题是,这会造成巨大的内存泄漏。泄漏发生在CreateBitmapSourceFromHBitmap 调用上,并在循环时填充内存,直到超过限制。如果我不使用那个调用,那么泄漏就会消失。知道为什么会这样吗?

【问题讨论】:

  • “内存泄漏”是什么意思?它应该被销毁后将引用保留在内存中吗?还是一个电话就能填满你的记忆?
  • 它保留引用。一次调用就可以了,但内存泄漏仍然存在。
  • 你看到this exact same question了吗?它有很多链接,但没有答案。
  • 尝试另一种方法:将位图保存到 MemoryStream 并从该流中解码 BitmapImage 或 BitmapFrame(如 StackOverflow 上的多个帖子所示)
  • 我尝试了here的方法,包括包装类,它也泄漏了内存。

标签: c# .net wpf memory-management memory-leaks


【解决方案1】:

在返回之前尝试在您的 BitmapSource 上调用 Freeze()。这似乎有助于释放对图像字节的一些引用。

[DllImport("gdi32")]
static extern int DeleteObject(IntPtr o);
public BitmapSource loadBitmap(System.Drawing.Bitmap source)
{
    IntPtr ip = source.GetHbitmap();
    BitmapSource bs = null;
    try
    {
        bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                 ip, IntPtr.Zero, Int32Rect.Empty,
                 System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
    }
    finally
    {
        DeleteObject(ip);
    }

    bs.Freeze();
    return bs;
}

【讨论】:

    【解决方案2】:

    如果您有内存泄漏,您应该检查的第一件事是,是否有任何未释放的 Disposable 对象。

    我可以看到您没有同时处理 ImageGraphics 实例,这可能会导致内存泄漏。

    确保在使用结束时将它们丢弃。

    例如:在updateImagePreview 方法中

    using (Graphics gr = Graphics.FromImage(bmp))
    {
       ....
    }
    

    例如:在loadBitmap 方法中

    public BitmapSource loadBitmap(System.Drawing.Bitmap source)
    {
        ....
    
        finally
        {
            DeleteObject(ip);
            source.Dispose();
        }
    
        return bs;
    }
    

    【讨论】:

      【解决方案3】:

      我以某种方式解决了它,但我不知道我是如何摆脱那个泄漏的。我假设调用导致线程内存泄漏的代码以某种方式解决了该问题。我强烈建议使用 cmets 中提到的stream wrapper,因为仅使用 MemoryStream 也会导致内存泄漏。无论如何,以下是不会导致上述内存泄漏的代码,对我来说就像一个魅力。

      Timer takeScreen;
      
      // This is a button to start the screen capturing
      private void Button_Play_Click(object sender, RoutedEventArgs e)
          {
              int fps = 30;
              takeScreen = new Timer(o => addNewImage(), null, 0, 1000 / fps);
          }
      
      private void addNewImage()
          {
              using (Bitmap bmp = new Bitmap(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height))
              {
                  using (Graphics gr = Graphics.FromImage(bmp))
                  {
                      gr.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(bmp.Width, bmp.Height));
                      Image_Preview.Dispatcher.Invoke(new Action(() => Image_Preview.Source = loadBitmap(bmp)));
                  }
              }
          }
      
      public BitmapSource loadBitmap(System.Drawing.Bitmap source)
          {
              BitmapSource bmpf = null;
      
              using (MemoryStream ms = new MemoryStream())
              {
                  using (WrappingStream ws = new WrappingStream(ms))
                  {
                      source.Save(ws, System.Drawing.Imaging.ImageFormat.Bmp);
                      bmpf = BitmapFrame.Create(ws, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
                  }
              }
      
              return bmpf;
          }
      

      【讨论】:

        猜你喜欢
        • 2010-12-05
        • 2011-10-22
        • 1970-01-01
        • 2013-06-05
        • 2021-01-27
        相关资源
        最近更新 更多