为什么GC使以下代码片段失败

时间:2012-02-16 13:09:06

标签: c# .net image bitmap unmanaged

我对.NET如何管理图像有点困惑,我有以下代码,从非托管HBitmap构建托管位图,以维护alpha通道。

    public static Bitmap GetBitmapFromHBitmap(IntPtr nativeHBitmap)
    {
        Bitmap bmp = Bitmap.FromHbitmap(nativeHBitmap);

        if (Bitmap.GetPixelFormatSize(bmp.PixelFormat) < 32)
            return bmp;

        BitmapData bmpData;

        if (IsAlphaBitmap(bmp, out bmpData))
        {
            // MY QUESTION IS RELATED TO THIS
            // IF CALL SUPPRESS_FINALIZE THE OBJECT
            // IT WILL WORK, OTHERWISE IT FAILS
            GC.SuppressFinalize(bmp);

            return new Bitmap(
                bmpData.Width,
                bmpData.Height,
                bmpData.Stride,
                PixelFormat.Format32bppArgb,
                bmpData.Scan0);
        }

        return bmp;
    }

    private static bool IsAlphaBitmap(Bitmap bmp, out BitmapData bmpData)
    {
        Rectangle bmpBounds = new Rectangle(0, 0, bmp.Width, bmp.Height);

        bmpData = bmp.LockBits(bmpBounds, ImageLockMode.ReadOnly, bmp.PixelFormat);

        try
        {
            return IsAlphaBitmap(bmpData);
        }
        finally
        {
            bmp.UnlockBits(bmpData);
        }
    }

    private static bool IsAlphaBitmap(BitmapData bmpData)
    {
        for (int y = 0; y <= bmpData.Height - 1; y++)
        {
            for (int x = 0; x <= bmpData.Width - 1; x++)
            {
                Color pixelColor = Color.FromArgb(
                    Marshal.ReadInt32(bmpData.Scan0, (bmpData.Stride * y) + (4 * x)));

                if (pixelColor.A > 0 & pixelColor.A < 255)
                {
                    return true;
                }
            }
        }

        return false;
    }

好的,我知道第GC.SuppressFinalize(bmp);行是没有意义的,但是当我删除该行时,有时(每4或5次调用)我会得到以下异常:

  

尝试读取或写入受保护的内存。这通常是一个   表明其他内存已损坏。

我怀疑垃圾是在绘制返回位图之前收集bmp对象,因此它尝试访问框架处理的位。如果我从不收集bmp它可以工作,但它会导致内存泄漏(从不收集bmp引用)。

你知道我怎么能解决这个问题?

1 个答案:

答案 0 :(得分:4)

查看Bitmap constructor you are using

的重新制作者
  

调用者负责分配和释放scan0参数指定的内存块。 但是,在释放相关的位图之前,不应释放内存。

这意味着在释放bmpData返回的Bitmap实例之后,您需要确保保留GetBitmapFromHBitmap指向的基础内存块。< / p>

您的问题是由于垃圾收集器检测到bmp无法访问(未使用)而导致收集/最终确定肯定释放底层内存块,然而,即使您取消终结器,您仍然会调用UnlockBits,这意味着bmpData无论如何已经无效 - 它可能现在可以正常工作,但这完全是偶然的。为了使上面的代码正确,你需要找到一种机制,只要返回的bmpData实例存在,bmp(和扩展名Bitmap)有效 - 即可能是对您的申请进行重大更改。

另外,请参阅Converting Bitmap PixelFormats in C#,了解完全不同的做法(我认为)您想要实现的目标,同时完全避免所有这些问题。