Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy from IntPtr (16 bit) array to managed ushort

Tags:

c#

.net

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[] ]

like image 985
Dr.YSG Avatar asked Feb 18 '15 23:02

Dr.YSG


1 Answers

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.

like image 184
max Avatar answered Oct 30 '22 12:10

max