I have a IntPtr called rawbits, which points to a 10MB array of data, 16 bit values. I need to return a managed ushort array from that. The following code works but there is an extra BlockCopy I would like to get rid of. Marshal.Copy does not support ushort. What can I do? (FYI: the rawbits is filled in by a video framegrabber card into unmanaged memory)
public const int width = 2056; public const int height = 2048; public const int depth = 2; public System.IntPtr rawbits; public ushort[] bits() { ushort[] output = new ushort[width * height]; short[] temp = new short[width * height]; Marshal.Copy(rawbits, temp, 0, width * height); System.Buffer.BlockCopy(temp, 0, output, 0, width * height * depth); return output; }
The suggestions given in the following question did not help. (compiler error).
C# Marshal.Copy Intptr to 16 bit managed unsigned integer array
[BTW, the short array does have unsigned 16 bit data in it. The Marshal.Copy() does not respect the sign, and that is what I want. But I would rather not just pretend that the short[] is a ushort[] ]
Option 1 - call CopyMemory
:
[DllImport("kernel32.dll", SetLastError = false)] static extern void CopyMemory(IntPtr destination, IntPtr source, UIntPtr length); public static void Copy<T>(IntPtr source, T[] destination, int startIndex, int length) where T : struct { var gch = GCHandle.Alloc(destination, GCHandleType.Pinned); try { var targetPtr = Marshal.UnsafeAddrOfPinnedArrayElement(destination, startIndex); var bytesToCopy = Marshal.SizeOf(typeof(T)) * length; CopyMemory(targetPtr, source, (UIntPtr)bytesToCopy); } finally { gch.Free(); } }
Not portable, but has nice performance.
Option 2 - unsafe
and pointers:
public static void Copy(IntPtr source, ushort[] destination, int startIndex, int length) { unsafe { var sourcePtr = (ushort*)source; for(int i = startIndex; i < startIndex + length; ++i) { destination[i] = *sourcePtr++; } } }
Requires unsafe
option to be enabled in project build properties.
Option 3 - reflection (just for fun, don't use in production):
Marshal
class internally uses CopyToManaged(IntPtr, object, int, int)
method for all Copy(IntPtr, <array>, int, int)
overloads (at least in .NET 4.5). Using reflection we can call that method directly:
private static readonly Action<IntPtr, object, int, int> _copyToManaged = GetCopyToManagedMethod(); private static Action<IntPtr, object, int, int> GetCopyToManagedMethod() { var method = typeof(Marshal).GetMethod("CopyToManaged", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); return (Action<IntPtr, object, int, int>)method.CreateDelegate( typeof(Action<IntPtr, object, int, int>), null); } public static void Copy<T>(IntPtr source, T[] destination, int startIndex, int length) where T : struct { _copyToManaged(source, destination, startIndex, length); }
Since Marshal
class internals can be changed, this method is unreliable and should not be used, though this implementation is probably the closest to other Marshal.Copy()
method overloads.
Seems like you're stuck with either doing the extra conversion yourself (short[] to ushort[], the one you basically do already), or doing the mem copy yourself through unsafe keyword.
There's third option: create custom struct.
struct MyMagicalStruct { // todo: set SizeConst correct [MarshalAs(UnmanagedType.ByValArray, SizeConst=width*height)] public ushort[] Test123; }
You'd also have to use Marshal.PtrToStructure<MyMagicalStruct>(yourPtr)
..