سؤال

I have some code which has the error "AccessViolationException was unhandled by user code: Attempted to read or write protected memory..."

A trimmed down version of the offending function is as follows:

protected override void OnPaint(PaintEventArgs pe)
{
    if ((updatingFastBackground) || (Calculating)) return; //ADDED FOR DEBUGGING, SEE BELOW
            BitmapData canvasData = Canvas.LockBits(new Rectangle(Point.Empty, Canvas.Size), ImageLockMode.WriteOnly, FastPixelFormat);
            BitmapData fbgData = fastBackground.LockBits(new Rectangle(Point.Empty, fastBackground.Size), ImageLockMode.ReadOnly, FastPixelFormat);
            try
            {
                unsafe
                {
                    byte* canvasDataScan0 = (byte*)canvasData.Scan0.ToPointer();
                    byte* fbgDataScan0 = (byte*)fbgData.Scan0.ToPointer();
                    Rectangle spriteBounds = new Rectangle(Point.Empty, ButtonImages.ImageSize);
                    for (int i = 0; i < ButtonImages.Images.Count; i++)
                    {
                        // Button offset location
                        Point l = new Point(
                            (int)((i % columnCount) * hStep + myIVM.Location.X),
                            (int)((i / columnCount) * vStep + myIVM.Location.Y));
                        // Paint at current location?
                        if (buttonPaintBounds.Contains(l))
                        {
                            BitmapData spriteData = buttonBitmaps[i].LockBits(spriteBounds, ImageLockMode.ReadOnly, FastPixelFormat);
                            try
                            {
                                int spriteLeft = Math.Max(l.X, 0);
                                int spriteRight = Math.Min(l.X + ButtonImages.ImageSize.Width, canvasData.Width);
                                int spriteTop = Math.Max(l.Y, 0);
                                int spriteBottom = Math.Min(l.Y + ButtonImages.ImageSize.Height, canvasData.Height);
                                int spriteWidth = spriteRight - spriteLeft;
                                int spriteHeight = spriteBottom - spriteTop;
                                byte* canvasRowLeft = canvasDataScan0 + (spriteTop * canvasData.Stride) + spriteLeft * 4;
                                byte* spriteRowLeft =
                                    (byte*)spriteData.Scan0.ToPointer() +
                                    Math.Max((spriteTop - l.Y), 0) * spriteData.Stride +
                                    Math.Max((spriteLeft - l.X), 0) * 4;
                                for (int y = 0; y < spriteHeight; y++)
                                {
                                    canvasRowLeft += canvasData.Stride;
                                    spriteRowLeft += spriteData.Stride;
                                    Byte* canvasWalk = (Byte*)canvasRowLeft;
                                    Byte* spriteWalk = (Byte*)spriteRowLeft;
                                    for (int x = 0; x < spriteWidth; x++)
                                    {
                                        if (spriteWalk[3] != 255)
                                        {
                                            canvasWalk[0] = (byte)(canvasWalk[0] * spriteWalk[3] / 255 + spriteWalk[0]);
                                            canvasWalk[1] = (byte)(canvasWalk[1] * spriteWalk[3] / 255 + spriteWalk[1]);
                                            canvasWalk[2] = (byte)(canvasWalk[2] * spriteWalk[3] / 255 + spriteWalk[2]);
                                        }
                                        canvasWalk += 4;
                                        spriteWalk += 4;
                                    }
                                }
                                thesePoints.Add(l);
                            }
                            finally
                            {
                                buttonBitmaps[i].UnlockBits(spriteData);
                            }
                        }

The error occurs on the line:

canvasWalk[0] = (byte)(canvasWalk[0] * spriteWalk[3] / 255 + spriteWalk[0]);

and even when replaced with:

canvasWalk[0] = 0;

The iteration variables y and x have different values each time it crashes so this leads me to believe an external function is modifying the Canvas bitmap.

IF this is in fact my problem, is there a way to prevent fastBackground and Canvas from being externally modified? I thought LockBits was supposed to do that that...

If that's not enough to answer, here's some more I've tried: I added the line

if ((updatingFastBackground) || (Calculating)) return;

to exit OnPaint if fastBackground Canvas or dimensions are being modified by other functions.

I could use a mutex to prevent the functions that modify the bitmaps fastBackground and Canvas from being run at the same time as paint (as I think they must be) but I'd rather block them another way as Canvas is public and I don't want to require passing a mutex out of the class.

per @usr 's suggestion, this further trimmed down version doesn't fail... Must have been a PTD error. (programmer too dumb) i.e. arithmetic error

protected override void OnPaint(PaintEventArgs pe)
{
    if ((updatingFastBackground) || (Calculating)) return; //ADDED FOR DEBUGGING, SEE BELOW
            BitmapData canvasData = Canvas.LockBits(new Rectangle(Point.Empty, Canvas.Size), ImageLockMode.WriteOnly, FastPixelFormat);
            BitmapData fbgData = fastBackground.LockBits(new Rectangle(Point.Empty, fastBackground.Size), ImageLockMode.ReadOnly, FastPixelFormat);
            try
            {
                unsafe
                {
                    byte* canvasDataScan0 = (byte*)canvasData.Scan0.ToPointer();
                    byte* fbgDataScan0 = (byte*)fbgData.Scan0.ToPointer();
                    Rectangle spriteBounds = new Rectangle(Point.Empty, ButtonImages.ImageSize);
                    for (int i = 0; i < ButtonImages.Images.Count; i++)
                    {
                        // Button offset location
                        Point l = new Point(
                            (int)((i % columnCount) * hStep + myIVM.Location.X),
                            (int)((i / columnCount) * vStep + myIVM.Location.Y));
                        // Paint at current location?
                        if (buttonPaintBounds.Contains(l))
                        {
                            BitmapData spriteData = buttonBitmaps[i].LockBits(spriteBounds, ImageLockMode.ReadOnly, FastPixelFormat);
                            try
                            {
                                byte* canvasRowLeft = canvasDataScan0;
                                byte* spriteRowLeft = (byte*)spriteData.Scan0.ToPointer();
                                for (int y = 0; y < 145; y++)
                                {
                                    canvasRowLeft += canvasData.Stride;
                                    spriteRowLeft += spriteData.Stride;
                                    Byte* canvasWalk = (Byte*)canvasRowLeft;
                                    Byte* spriteWalk = (Byte*)spriteRowLeft;
                                    for (int x = 0; x < 145; x++)
                                    {
                                        if (spriteWalk[3] != 255)
                                        {
                                            canvasWalk[0] = 0;
                                            canvasWalk[1] = 0;
                                            canvasWalk[2] = 0;
                                        }
                                        canvasWalk += 4;
                                        spriteWalk += 4;
                                    }
                                }
                                thesePoints.Add(l);
                            }
                            finally
                            {
                                buttonBitmaps[i].UnlockBits(spriteData);
                            }
                        }
هل كانت مفيدة؟

المحلول

Moving my comment into an answer because it helped solve the problem:

Fixed is not required for the buffer returned by LockBits because it is unmanaged memory. Your pointer arithmetic is wrong. Find the bug. Create a simple repro to help find the bug.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top