C#.NET 中的 Marshal.Copy 方法抛出 AccessViolationException
我正在开发一个 C# 应用程序,该应用程序可以显示来自相机的实时图像。 我在以下代码片段中面临的问题是,当运行在线程中连续执行的函数时,我在 Marshal.Copy 中得到 AccessViolationException。 但是,运行一次后就会成功运行(我得到一个静态图像)。 我想这与一些内存损坏问题有关。 关于如何处理这个问题有什么想法/建议吗?
private Image ByteArrayToImage(byte[] myByteArray)
{
if (myByteArray != null)
{
MemoryStream ms = new MemoryStream(myByteArray);
int Height = 504;
int Width = 664;
Bitmap bmp = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(myByteArray, 0, bmpData.Scan0, myByteArray.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
return null;
}
I am working on a C# application that would display live images from a camera.
The problem I am facing with the following code snippet is that, I get AccessViolationException in Marshal.Copy when running this function executed continuously in a thread. But, this runs successfully when run once (I get a single static image). I guess it has to do with some memory corruption issue. Any idea/suggestions on how to deal with this problem?
private Image ByteArrayToImage(byte[] myByteArray)
{
if (myByteArray != null)
{
MemoryStream ms = new MemoryStream(myByteArray);
int Height = 504;
int Width = 664;
Bitmap bmp = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(myByteArray, 0, bmpData.Scan0, myByteArray.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
return null;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
在我看来,您总是试图将字节数 myByteArray.Length 复制到位图缓冲区。
您没有检查位图缓冲区实际上有那么大 - 因此可能会注销位图缓冲区的末尾。
尝试检查 myByteArray.Length 是否大于 bmpData.Stride x bmp.Height
如果是这种情况,您需要重新审视您所做的假设宽度、高度和像素格式的硬编码值。
It looks to me like you are always trying to copy the number of bytes myByteArray.Length to the bitmap buffer.
You are not checking that the bitmap buffer is in fact as big as that - so are probably writing off the end of the bitmap buffer.
Try checking if myByteArray.Length is ever greater than bmpData.Stride x bmp.Height
If this is the case you'll need to relook at the assumptions you've made with your hard coded values for width, height and pixel format.
您不应该一次复制整个图像。 位图对象的内存分配可能不是您所期望的。 例如,第一扫描线可能最后存储在存储器中,这意味着第二扫描线的数据将最终位于为位图对象分配的存储器区域之外。 此外,扫描线之间可能有填充,以将它们放置在偶数地址上。
一次复制一行,使用 bmpData.Stride 查找下一个扫描行:
You shouldn't copy the entire image at once. The memory allocation of the bitmap object might not be what you expect. For example the first scan line may be stored last in memory, which would mean that the data for the second scan line would end up outside the allocated memory area for the bitmap object. Also there may be padding between the scan lines to place them on an even address.
Copy one line at a time, using bmpData.Stride to find the next scan line:
我个人见过一些堆损坏(调试故障转储),因为使用了 .Length 。
如:
堆损坏的解决方案是以不同的方式计算 .Length:
字节和 .Length 有 1 个字节的差异,导致堆损坏。
Math.Abs直接取自微软的例子。
因为对于自下而上的位图,步长可以为负。
微软示例: https://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.scan0%28v=vs.110%29.aspx?f= 255&MSPPError=-2147217396#Examples
(+不要忘记 .Unlock 并将其添加到 try-finally 语句中。)
I've personally seen some heap corruptions (debugging the crash dumps) because .Length was used.
As in:
The solution to the heap corruption was to do calculate the .Length differently:
bytes and .Length had 1 byte difference, resulting in the heap corruption.
Math.Abs was taken directly from the example of Microsoft.
Because the Stride can be negative for a bottom-up bitmap.
Example microsoft: https://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.scan0%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396#Examples
(+ don't forget the .Unlock and add it in a try-finally statement.)
为我回答:忘记了 ->
有没有人弄清楚这一点? 这是第四页,没有答案。 使用 msdn 中的确切代码: http://msdn .microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx 这是:
这在优化模式下不起作用,并且不作为 IDE 中的 exe 运行。 有任何想法吗
我试图把这是一个新项目,如果我在按下按钮时附加到进程,并多次按下此按钮,则会发生错误,但在我的代码中,我只调用一次,无论哪种方式都不确定为什么会出现错误。
Answer for me: forgot to ->
Has anyone figured this out? This is about the fourth page without an answer. Using the exact code from msdn: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx which is:
This doesn't work in optimize mode and running as exe not in IDE. Any ideas
I tried to put this is a new project and if I attached to process when i push a button, and press this button multiple times the error occurs, but in my code I'm only calling once, either way not sure why the error.
也许
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat); 的
只读参数无效 - 尝试 ReadWrite,或 ImageLockMode 中的只读?
也许这有帮助。
Maybe
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
has invalid argument for write only - try ReadWrite, or ReadOnly in ImageLockMode?
Maybe that helps.
我搜索了一下,如果我们跳过数组大小不正确的可能性,我们将以 BitmapData.Stride 属性备注:
,也许我们应该这样做:
但我想知道:我们已经创建了位图:
new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
...那么,怎么可能是负数呢?是时候进行 ILSpy 了:
它指出了我此处 和此处:
传递0是什么意思?不知道,找不到。 任何人都可以吗? 可以用负步幅创建位图吗? (通过 .NET
new Bitmap(Width, Height, PixelFormat.Format24bppRgb)
)。 无论如何,我们至少需要检查BitmapData.Stride。I was searching a bit and if we skip the possibility that the array is not of the proper size, we end in Remarks for BitmapData.Stride Property:
So, maybe we should do it this way:
But I wondered: We have created the bitmap:
new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
...so, how could it possibly be negative?Time for ILSpy:
And it pointed me here and here:
What does it mean to pass 0? Don't know, couldn't find it. Can anybody? Can the bitmap be created with negative stride? (by .NET
new Bitmap(Width, Height, PixelFormat.Format24bppRgb)
). In any case, we need to at least check BitmapData.Stride.我在 BitmapSource 类。 似乎值得尝试使用下面的代码创建一个数组,并尝试多次执行复制方法而不发生爆炸。 如果可以的话,这很可能是一个数组大小问题。 如果相机中的数据与内存中位图的大小不匹配,您可能必须逐行复制数据。
您应该做的另一件事是确保位图对象在完成后被正确处置。 我偶尔会看到使用非托管代码操作且事后未清理的对象的奇怪结果。
此外,跨线程传输对象有时可能很棘手。 请参阅如何:进行线程安全调用Windows 窗体控件 和 在 C# 中使用 Windows 窗体控件进行线程安全调用一文。
I found the rawStride formula in the code example for the BitmapSource Class. It seems worth trying creating an array using the code below and attempting to execute your copy method multiple times without it bombing. If you can there is a fair chance that this is an array sizing problem. If the data from your camera doesn't match the size of the bitmap in memory, you'll probably have to copy the data line by line.
The other thing you should do is make sure that Bitmap object is being Disposed properly when you finish with it. I have occasionally seen weird results with objects that have been operated on with unmanaged code and not cleaned up afterwards.
Also, transiting objects across threads can be tricky sometimes. See How to: Make Thread-Safe Calls to Windows Forms Controls and Thread-Safe Calls Using Windows Form Controls in C# article.
让我们尝试将 ThreadApartmentState 更改为单线程。
另外,检查是否有导致此错误的跨线程操作。
Lets try to change ThreadApartmentState to Single Threaded.
Also, check for cross-thread operations causing this errors.
它发生在我身上...
bmpData.Scan0 指向位图的第一行并且
bmpData.Stride 是一行的字节数。 大多数时候它是负数。
这意味着位图的第二行位于该地址
bmpData.Scan0 - MAth.abs(bmpData.Stride)
位图的第 n 行位于该地址处
bmpData.Scan0 - n * Math.Abs(bmpData.Stride)
因此,如果您从 bmpData.Scan0 复制不止一行,则会出现异常
It happens to me...
bmpData.Scan0 point to the first line of the bitmap and
bmpData.Stride is the number of bytes of a line. It is most of the time a negative number.
it means that the second line of the bitmap is at the address
bmpData.Scan0 - MAth.abs(bmpData.Stride)
the n line of the bitmap is at the address
bmpData.Scan0 - n * Math.Abs(bmpData.Stride)
so if you copy from bmpData.Scan0 more than one line , you have an exception