从IntPtr(16位)数组复制到托管的ushort

Dr.*_*YSG 6 .net c#

我有一个名为rawbits的IntPtr,它指向10MB的数据数组,16位值.我需要从中返回一个托管的ushort数组.以下代码有效,但我想摆脱一个额外的BlockCopy.Marshal.Copy不支持ushort.我能做什么?(仅供参考:rawbits由视频framegrabber卡填充到非托管内存中)

    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;
}
Run Code Online (Sandbox Code Playgroud)

以下问题中给出的建议没有帮助.(编译错误).

C#Marshal.Copy Intptr到16位托管无符号整数数组

[顺便说一句,短阵列中确实有无符号的16位数据.Marshal.Copy()不尊重标志,这就是我想要的.但我宁愿不只是假装短[]是一个ushort []]

max*_*max 8

选项1 - 致电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();
    }
}
Run Code Online (Sandbox Code Playgroud)

不便携,但性能不错.


选项2 - unsafe和指针:

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++;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

需要unsafe在项目构建属性中启用选项.


选项3 - 反思(只是为了好玩,不要在生产中使用):

Marshalclass内部使用CopyToManaged(IntPtr, object, int, int)所有Copy(IntPtr, <array>, int, int)重载的方法(至少在.NET 4.5中).使用反射我们可以直接调用该方法:

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);
}
Run Code Online (Sandbox Code Playgroud)

由于Marshal可以更改类内部,因此该方法不可靠且不应使用,但此实现可能最接近其他Marshal.Copy()方法重载.