首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >从IntPtr (16位)数组复制到托管ushort

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

Stack Overflow用户
提问于 2015-02-18 23:21:34
回答 3查看 6.4K关注 0票数 10

我有一个名为rawbit的IntPtr,它指向一个10 16的数据数组,16位值。我需要返回一个托管的ushort数组。下面的代码可以工作,但有一个额外的BlockCopy我想摆脱。Marshal.Copy不支持ushort。我能做什么?(FYI: rawbit由视频帧抓取卡填充到非托管内存中)

代码语言:javascript
运行
复制
    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[] ]

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-02-19 02:45:27

选项1-调用CopyMemory

代码语言:javascript
运行
复制
[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和指针:

代码语言:javascript
运行
复制
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, <array>, int, int)重载使用CopyToManaged(IntPtr, object, int, int)方法(至少在.NET 4.5中如此)。使用反射,我们可以直接调用该方法:

代码语言:javascript
运行
复制
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()方法重载最接近的实现。

票数 12
EN

Stack Overflow用户

发布于 2015-02-18 23:35:52

看起来您要么自己做额外的转换(short[]到ushort[],基本上已经做了),要么通过不安全关键字复制mem。

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

代码语言:javascript
运行
复制
struct MyMagicalStruct
{
    // todo: set SizeConst correct
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=width*height)] 
    public ushort[] Test123;
}

您还必须使用Marshal.PtrToStructure<MyMagicalStruct>(yourPtr)

票数 0
EN

Stack Overflow用户

发布于 2020-09-21 14:18:06

在现代.NET中,您可能能够以跨范围而不是数组的方式工作,然后事情变得有趣起来:

代码语言:javascript
运行
复制
public unsafe Span<ushort> bits()
    => new Span<short>(rawbits.ToPointer(), width * height);

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

如果调用方不需要(或不应该)更改值,则可以使用ReadOnlySpan<T>

注意:如果您使用unsafe构造一个span,并且出错(例如,长度),那么它仍然会在调用者身上爆炸,因此是“安全的”而不是安全的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28595903

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档