我有一个函数,它从像素数组构建图像并将其保存到文件中。调用一次它没有问题。但是,如果我将它调用两次(在第一次完全执行之后),该函数将抛出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
成功。原因是什么?
答案 0 :(得分:3)
此错误表明您正在尝试访问受保护的内存。很可能你传递的数组比你需要的图像小。它类似于缓冲区溢出。因此,当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");
}
答案 1 :(得分:0)
UPD:
System.Drawing.Image bmp = new System.Drawing.Bitmap(960, 540, 960 * 540 * 3, PixelFormat.Format24bppRgb, arrayHandle.AddrOfPinnedObject());
答案 2 :(得分:-2)
我也有这样的问题,但在这种情况下,唯一的方法是有效的。
[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异常,因此您需要添加[HandleProcessCorruptedStateExceptions]属性