【问题标题】:Copy from IntPtr (16 bit) array to managed ushort从 IntPtr(16 位)数组复制到托管 ushort
【发布时间】:2015-02-18 23:21:34
【问题描述】:

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

    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;
}

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

C# Marshal.Copy Intptr to 16 bit managed unsigned integer array

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

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    选项 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();
        }
    }
    

    不便携,但性能不错。


    选项 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++;
            }
        }
    }
    

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


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

    Marshal 类在内部对所有 Copy(IntPtr, &lt;array&gt;, int, int) 重载使用 CopyToManaged(IntPtr, object, 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);
    }
    

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

    【讨论】:

    • 将数组转换为指针时应该使用fixed
    • fixed 不能用于创建通用的Copy&lt;T&gt;() 方法,因为fixed(T* ptr = array) 不会编译。没有通用的约束允许这样做。同样fixed 需要unsafe 选项,而使用GCHandle 不需要。
    • 很好的答案。任何直觉作为按性能排序的排名? [并不是说我在正常模式下将这种转换为托管空间,而是为了调试,将 25 fps 的流量转移到主机而不是发送到 GPU 进行处理很有用)。
    • 所有这些选项的性能应该相似。我的赌注 - CopyMemory() 是最快的,但你绝对应该测试一下。
    • CopyMemorylength 参数应该是uint 类型,而不是UIntPtr!否则,在 64 位平台上签名将不正确
    【解决方案2】:

    似乎你要么自己做额外的转换(short[] 到 ushort[],你基本上已经做的那个),或者通过 unsafe 关键字自己做 mem 复制。

    还有第三种选择:创建自定义结构。

    struct MyMagicalStruct
    {
        // todo: set SizeConst correct
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=width*height)] 
        public ushort[] Test123;
    }
    

    您还必须使用Marshal.PtrToStructure&lt;MyMagicalStruct&gt;(yourPtr)..

    【讨论】:

      【解决方案3】:

      在现代 .NET 中,您可能可以使用 span 而不是数组,然后事情就变得有趣了:

      public unsafe Span<ushort> bits()
          => new Span<short>(rawbits.ToPointer(), width * height);
      

      这是零拷贝,但不会传播unsafe - 消费者可以在托管/“安全”代码中访问跨度。它的工作原理很像一个数组,包括边界省略等 - 但可以与非托管内存通信。

      如果调用者不需要(或不应该)更改值,您可以使用 ReadOnlySpan&lt;T&gt;

      注意:如果您使用unsafe 构造一个跨度并弄错了(例如长度),那么它仍然可以在调用者上爆炸,因此是“安全”而不是安全。

      【讨论】:

        猜你喜欢
        • 2015-01-25
        • 2011-03-13
        • 2013-04-05
        • 1970-01-01
        • 2023-03-03
        • 2019-03-23
        • 2011-11-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多