Повреждение памяти с помощью LockBits (веб-камера DirectShow)

Я делаю некоторые манипуляции с изображениями в .NET (VB 2010). Я использую следующий код (который еще ничего не делает):

Sub Manipulate(IMG As Bitmap)
    ' Dim foo(100000) As Integer  - will need it later...

    Dim bd = IMG.LockBits(New Rectangle(0, 0, IMG.Width, IMG.Height),
                          Imaging.ImageLockMode.ReadWrite,
                          Imaging.PixelFormat.Format24bppRgb)
    Dim absstride = Math.Abs(bd.Stride)
    Dim numbytes = absstride * IMG.Height
    Dim bytes(numbytes - 1) As Byte
    Dim flipped = bd.Stride < 0
    Dim ptr = If(flipped, bd.Scan0 - numbytes + absstride, bd.Scan0)
    System.Runtime.InteropServices.Marshal.Copy(ptr, bytes, 0, numbytes)

    ' I'm going to put sg here

    System.Runtime.InteropServices.Marshal.Copy(bytes, 0, ptr, numbytes)
    IMG.UnlockBits(bd)
End Sub

Это прекрасно работает для простых целей (например, для увеличения яркости изображения), но моему алгоритму нужны большие переменные (см. «foo» выше). При объявлении (раскомментируйте эту строку) я внезапно получаю исключения:
в зависимости от размера 'foo'...

...либо первый Marshal.Copy() выдает
AccessViolationException: Попытка чтения или записи в защищенную память. Это часто указывает на то, что другая память повреждена.

...или объявление 'bytes' throws
FatalExecutionEngineError: Среда выполнения обнаружила фатальную ошибку. Адрес ошибки был 0x6819d142, в потоке 0x3690. Код ошибки 0xc0000005. Эта ошибка может быть ошибкой в ​​среде CLR или в небезопасных или непроверяемых частях пользовательского кода. Распространенными источниками этой ошибки являются ошибки маршалинга пользователя для COM-interop или PInvoke, которые могут повредить стек.

...или вообще никаких исключений и все работает нормально.

Что здесь происходит?

Дополнительная информация: я получаю объекты изображения с веб-камеры с помощью DirectShow.


person Dave    schedule 03.08.2012    source источник
comment
Избавьтесь от перевернутого, растровое изображение никогда не запускается до Scan0. И никогда не запускайте этот код на растровом изображении, которое вы получили от обратного вызова, который больше не выполняется. Если вы не сделаете глубокую копию растрового изображения с помощью конструктора Bitmap(Image).   -  person Hans Passant    schedule 06.08.2012
comment
@HansPassant Теоретически я знаю, что всегда должен копировать из Scan0, но это просто не сработает, если перевернуть это правда. Таким образом, я копирую из правильного места в памяти, я проверил это с помощью Bitmap.GetPixel().   -  person Dave    schedule 07.08.2012
comment
@HansPassant Создание глубокой копии решает проблему, спасибо :)   -  person Dave    schedule 07.08.2012


Ответы (1)


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

person Dave    schedule 07.08.2012