C#镜像.保存AccessViolationException

本文关键字:AccessViolationException 保存 镜像 | 更新日期: 2023-09-27 17:57:34

我有一个函数,它从像素阵列构建图像并将其保存到文件中。调用一次就可以正常工作。但是,如果我调用它两次(在第一次完全执行之后),函数就会抛出AccessViolationException

private void saveImage(byte[] bmpBytes)
{
    Debug.Print("Image saving started");
    var arrayHandle = System.Runtime.InteropServices.GCHandle.Alloc(bmpBytes, System.Runtime.InteropServices.GCHandleType.Pinned);
    System.Drawing.Image bmp = new System.Drawing.Bitmap(960, 540, 960 * 3, PixelFormat.Format24bppRgb, arrayHandle.AddrOfPinnedObject());
    bmp.Save("img.bmp");
    Debug.Print("Image saving completed");
}

因此,bmp.Save("img.bmp");行抛出异常。我也尝试过将数据保存在MemoryStream中,但结果相同:第一次调用成功,每隔一次保存一次AccessViolationException。原因可能是什么?

C#镜像.保存AccessViolationException

此错误表示您正在尝试访问受保护的内存。很可能你通过了一个比你需要的图像更小的数组。这类似于缓冲区溢出。因此,当GDI试图用您的映像读取内存时,它超出了为进程内存分配的范围,就会发生故障。顺便说一句,行为是未定义的。例如,如果这段内存已经分配给了一个进程,那么您将读取它所拥有的内容,并且不会收到任何错误。在这种情况下,您很可能会看到带有一些噪声的图像。

你确定你正在传递一个有效长度的数组吗?尝试添加相应的检查。不要忘记释放已分配的内存-当不再需要GCHandle时,它必须与Free一起释放。

只是运行了那个代码,没有出现任何错误:

int width = 960;
int height = 540;
void Main()
{
    var arr = Enumerable.Range(0, width * height * 3).Select(i =>
    {
        i = i / 3;
        var y = i / width;
        var x = i - y * width;
        var xd = x / (double)(width - 1);
        var yd = y / (double)(height - 1);
        return (byte)((xd + yd) / 2d * 255);
    }).ToArray();
    saveImage2(arr);
}
private void saveImage2(byte[] bmpBytes)
{
    if (bmpBytes == null)
        throw new ArgumentNullException("bmpBytes");
    if (bmpBytes.Length != width * height * 3)
        throw new ArgumentException("Invalid array length", "bmpBytes");
    var output = new Bitmap(width, height, PixelFormat.Format24bppRgb);
    var rect = new Rectangle(0, 0, width, height);
    var bmpData = output.LockBits(rect, ImageLockMode.WriteOnly, output.PixelFormat);
    // Stride must be taken into account.
    // Actual row length may be greater then required due to GDI's internal memory alignment.
    // It is an error to copy entire array as-is, need to copy row-by-row.
    var rowBytes = width * Image.GetPixelFormatSize(output.PixelFormat) / 8;
    var ptr = bmpData.Scan0;
    for (var i = 0; i < height; i++)
    {
        Marshal.Copy(bmpBytes, i * rowBytes, ptr, rowBytes);
        ptr += bmpData.Stride;
    }
    output.UnlockBits(bmpData);
    output.Save(@"d:'temp'img.bmp");
}

Upd:

System.Drawing.Image bmp = new System.Drawing.Bitmap(960, 540, 960 * 540 * 3, PixelFormat.Format24bppRgb, arrayHandle.AddrOfPinnedObject());

我也遇到过这样的问题,但在这种情况下,只有一种方法是有效的。

[HandleProcessCorruptedStateExceptions]
private void saveImage(byte[] bmpBytes)
{
    try {
     Debug.Print("Image saving started");
     var arrayHandle = System.Runtime.InteropServices.GCHandle.Alloc(bmpBytes, System.Runtime.InteropServices.GCHandleType.Pinned);
     System.Drawing.Image bmp = new System.Drawing.Bitmap(960, 540, 960 * 3, PixelFormat.Format24bppRgb, arrayHandle.AddrOfPinnedObject());
     bmp.Save("img.bmp");
     Debug.Print("Image saving completed");
    } catch ( System.AccessViolationException ex ) {
         // Doing smth
    }

}

在常见情况下,您无法捕获AccessViolationException异常,因此需要添加一个[HandProcessCorruptedStateExceptions]属性