Array write-protected by C# virtual machine
I'm writing a C# application, and I need part of it to be in C++ for speed efficiency. The function in C++ is exported as follow:
extern "C" __declspec(dllexport) int fastSegment(char*, int, int);
I import this function in C# as follow:
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static private extern bool fastSegment(byte[] img, int width, int height);
The image I want to process is called as follow:
fastSegment(image, 640, 480);
ima开发者_如何学JAVAge
is of the right size. Since I don't want to waste memory and allocate a new array, I modified the array directly in the C++ function.
What happens? Blue screen of death. I never saw it in Windows 7 before.
My function only wrote in image[0]
, image[1]
and image[2]
for testing purposes, and when I remove this everything is fine.
My guess is that the virtual machine protected the memory, but I find it strange I just can't write in it, or that the virtual machine didn't simply throw an exception. Is there a way to unprotect that buffer, or do I have to allocate a new one?
Edit:
It appiers the program runs now when I write in data. What could have been the cause of this sudden crash?
The garbage collector is allowed to move managed data. The native code is not able to detect this, so it is possible that it writes to the wrong memory address. But you can tell the .NET runtime not to move your array. E.g. with the GCHandle
class:
GCHandle handle = GCHandle.Alloc(image, GCHandleType.Pinned);
try
{
fastSegment(handle.AddrOfPinnedObject(), 640, 480);
}
finally
{
// make sure to free the handle to avoid leaks!
handle.Free();
}
Edit: This is only one way, but I think it illustrates the problem. Please read about Marshaling between Managed and Unmanaged Code
You can't do that. Your array will be readonly in this case.
If you want to change your C# array in native you need to marshal it as a pointer.
Have a look at the example from Microsoft.
There is also an explanation why this is needed.
It is possible that your data is getting moved around by the GC - but if you get the crash every time you run your application it's highly unlikely thats the case. Try one of the following:
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern bool fastSegment(IntPtr img, int width, int height);
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
static void FastSegment(byte[] data, int width, int height)
{
var length = width * height;
var ptr = Marshal.AllocHGlobal(width * height);
try
{
Marshal.Copy(data, 0, ptr, length);
fastSegment(ptr, width, height);
Marshal.Copy(ptr, data, 0, length);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
// ---- OR ----
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern bool fastSegment(IntPtr img, int width, int height);
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
static void FastSegment(byte[] data, int width, int height)
{
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
fastSegment(handle.AddrOfPinnedObject(), width, height);
}
finally
{
handle.Free();
}
}
// ---- OR ----
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static unsafe extern bool fastSegment(byte* img, int width, int height);
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
static void FastSegment(byte[] data, int width, int height)
{
unsafe
{
fixed (byte* dataPinned = data)
{
fastSegment(dataPinned, width, height);
}
}
}
精彩评论