C# Image.Save AccessViolationException

标签 c# image memory-management access-violation

我有一个函数,它从像素数组构建图像并将其保存到文件中。调用一次就可以正常工作。但是,如果我调用它两次(在第一次完全执行之后),该函数将抛出 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 都成功。可能是什么原因?

最佳答案

此错误表明您正在尝试访问 protected 内存。您传递的数组的大小很可能小于图像所需的大小。这与缓冲区溢出类似。因此,当 GDI 尝试使用图像读取内存时,它超出了为进程内存分配的范围,并且会发生故障。顺便说一句,行为是未定义的。例如,如果该内存块已分配给进程,您将读取它所拥有的任何内容并且不会收到错误。在这种情况下,您很可能会看到带有一些噪点的图像。

您确定要传递一个有效长度的数组吗?尝试添加相应的检查。并且不要忘记释放分配的内存 - GCHandle must be released with Free when it is no longer needed .

刚刚运行该代码,没有出现错误:

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

关于C# Image.Save AccessViolationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38459633/

相关文章:

C# - 水印密码字符?

java - 为什么 ResultSet 不从 MySQL 返回数据

c++ - 类数组正在破坏数据

ios - iphone 应用程序因内存压力而崩溃

Android - 获取图片资源

c - C中的bss段

c# - 计时器超时时清除剪贴板

c# - 具有取消能力的长时间运行操作模式

c# - 在我的计划循环中重新分配 DateTime 对象的值时,DateTime 多线程 UDP 发送方具有非常不同的时间结果

python - 如何去除图像的白色背景并使其透明?