0

I'm a little bit confusing about how .NET manages images, I have the following code, to build a managed bitmap form an unmanaged HBitmap, perserving the alpha channel.

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

Ok, I know that the line GC.SuppressFinalize(bmp); has no sense, but when I remove that line, sometimes (each 4 or 5 calls) I get the following exception:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

I suspect that the garbage is collecting the bmp object before the return bitmap is drawn, so it try to access to the bits that are disposed by the framework. If I never collect the bmp it works, but it causes a memory leak (the bmp reference is never collected).

Do you know how could I solve this issue?

Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219

1 Answers1

4

Take a look at the remakrs for the Bitmap constructor you are using

The caller is responsible for allocating and freeing the block of memory specified by the scan0 parameter. However, the memory should not be released until the related Bitmap is released.

This means that you need to make sure that you keep hold of the underlying block of memory that bmpData is pointing to until after the Bitmap instance returned by GetBitmapFromHBitmap is released.

Your problem is caused because the garbage collector has detected that bmp is unreachable (isn't being used) and so collects / finalizes it which definitely releases the underlying block of memory, however even if you suppress the finalizer you have still called UnlockBits which means that bmpData is already invalid anyway - it might work at the moment but thats completely down to chance. In order to make the above code correct you need to find a mechanism of keeping bmpData (and by extension bmp) valid for as long as the returned Bitmap instance is around - i.e. possibly a significant change to your application.

Alternatively see Converting Bitmap PixelFormats in C# for a completely different way of doing (I think) what you want to achieve while avoiding all of these problems entirely.

Community
  • 1
  • 1
Justin
  • 84,773
  • 49
  • 224
  • 367