Почему GC терпит неудачу следующий фрагмент кода - PullRequest
0 голосов
/ 16 февраля 2012

Я немного путаюсь с тем, как .NET управляет изображениями, у меня есть следующий код для создания управляемого растрового изображения из неуправляемого HBitmap, сохраняющего альфа-канал.

    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 Ответ

4 голосов
/ 16 февраля 2012

Посмотрите на римкеры для растрового конструктора, который вы используете

Вызывающий отвечает за выделение и освобождение блока памяти, указанного параметром scan0. Однако память не должна освобождаться до тех пор, пока не будет выпущено соответствующее растровое изображение.

Это означает, что вам нужно убедиться, что вы держите базовый блок памяти, на который указывает bmpData, до тех пор, пока не будет освобожден экземпляр Bitmap, возвращаемый GetBitmapFromHBitmap.

Ваша проблема вызвана тем, что сборщик мусора обнаружил, что bmp недоступен (не используется), и поэтому собирает / завершает его, что определенно освобождает базовый блок памяти, однако даже если вы подавляете финализатор, который вы все еще называете UnlockBits, что означает, что bmpData уже недействителен - он может работать в данный момент, но это совершенно случайно. Чтобы исправить приведенный выше код, вам нужно найти механизм поддержания bmpData (и с расширением bmp) действительным в течение всего времени существования возвращаемого экземпляра Bitmap, то есть, возможно, существенного изменения в вашем приложении.

В качестве альтернативы см. Преобразование растровых пиксельных форматов в C # для совершенно другого способа выполнения (я думаю) того, чего вы хотите достичь, избегая при этом всех этих проблем полностью.

...