c# - IntPtr 算法

标签 c# marshalling intptr

我尝试以这种方式分配一个结构数组:

struct T {
    int a; int b;
}

data = Marshal.AllocHGlobal(count*Marshal.SizeOf(typeof(T));
...

我想访问分配的数据,将结构“绑定(bind)”到分配的数组中的每个元素 与 AllocHGlobal...类似这样

T v;
v = (T)Marshal.PtrToStructure(data+1, typeof(T));

但我没有找到任何方便的方法...为什么 IntPtr 缺少算术?我怎样才能以“安全”的方式解决这个问题?

有人可以确认 PtrToStructure 函数将数据复制到结构变量中吗?换句话说,修改结构是否反射(reflect)了结构数组数据中的修改?

当然,我想使用结构对 IntPtr 指向的数据进行操作,而不是每次都复制数据,避免不安全的代码。

谢谢大家!

最佳答案

我可以想到四个选项,两个仅使用“安全”代码,两个使用不安全代码。不安全的选项可能要快得多。

安全:

  • 在托管内存中分配您的数组,并声明您的 P/Invoke 函数以获取该数组。即,而不是:

    [DllImport(...)]
    static extern bool Foo(int count, IntPtr arrayPtr);
    

    成功了

    [DllImport(...)]
    static extern bool Foo(int count, NativeType[] array);
    

    (我使用 NativeType 作为您的结构名称而不是 T,因为 T 通常在通用上下文中使用。)

    这种方法的问题是,据我所知,每次调用 Foo 时,NativeType[] 数组都会被编码两次。它将从托管内存复制到非托管内存 调用之前的内存,然后从非托管内存复制到托管内存。但是,如果 Foo 仅读取或写入数组,则可以对其进行改进。在这种情况下,用 [In](只读)或 [Out](只写)属性修饰 tarray 参数。这允许运行时跳过其中一个复制步骤。

  • 正如您现在所做的那样,在非托管内存中分配数组,并使用一系列对 Marshal.PtrToStructureMarshal.StructureToPtr 的调用。这可能会比第一个选项执行得更差,因为您仍然需要来回复制数组的元素,并且您是分步进行的,因此您有更多的开销。另一方面,如果数组中有很多元素,但在 Foo 调用之间只访问其中的一小部分,那么这可能会表现更好。您可能需要一些小的辅助函数,如下所示:

    static T ReadFromArray<T>(IntPtr arrayPtr, int index){
        // below, if you **know** you'll be on a 32-bit platform,
        // you can change ToInt64() to ToInt32().
        return (T)Marshal.PtrToStructure((IntPtr)(arrayPtr.ToInt64() +
            index * Marshal.SizeOf(typeof(T)));
    }
    // you might change `T value` below to `ref T value` to avoid one more copy
    static void WriteToArray<T>(IntPtr arrayPtr, int index, T value){
        // below, if you **know** you'll be on a 32-bit platform,
        // you can change ToInt64() to ToInt32().
        Marshal.StructureToPtr(value, (IntPtr)(arrayPtr.ToInt64() +
            index * Marshal.SizeOf(typeof(T)), false);
    }
    

不安全:

  • 在非托管内存中分配您的数组,并使用指针访问元素。这意味着所有使用数组的代码都必须在 unsafe block 中。

    IntPtr arrayPtr = Marhsal.AllocHGlobal(count * sizeof(typeof(NativeType)));
    unsafe{
        NativeType* ptr = (NativeType*)arrayPtr.ToPointer();
    
        ptr[0].Member1 = foo;
        ptr[1].Member2 = bar;
        /* and so on */
    }
    Foo(count, arrayPtr);
    
  • 在托管内存中分配您的数组,并在需要调用 native 例程时固定它:

    NativeType[] array = new NativeType[count];
    array[0].Member1 = foo;
    array[1].Member2 = bar;
    /* and so on */
    
    unsafe{
        fixed(NativeType* ptr = array)
            Foo(count, (IntPtr)ptr);
            // or just Foo(count, ptr), if Foo is declare as such:
            //     static unsafe bool Foo(int count, NativeType* arrayPtr);
    }
    

如果您可以使用不安全代码并且关心性能,最后一个选项可能是最干净的,因为您唯一的不安全代码是您调用 native 例程的地方。如果性能不是问题(也许如果数组的大小相对较小),或者如果您不能使用不安全的代码(也许您没有完全信任),那么第一个选项可能是最干净的,尽管,正如我所提到的,如果您在调用 native 例程之间访问的元素数量只占数组中元素数量的一小部分,那么第二个选项会更快。

注意事项:

不安全操作假定您的结构是 blittable .如果没有,那么安全例程是您唯一的选择。

关于c# - IntPtr 算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1318682/

相关文章:

json - 在 Jackson 中使用自定义编码器而不使用注释?

c# - 从另一个数组中删除一个数组的高效算法

java - 编码将 jlist 的菜单项保存到 XML 并解码加载菜单项

xml - Golang 更改 float 的 xml 编码格式

c# - 如何在非托管内存中分配 IntPtr [] 数组?

c# - 如何在 C# 中将 IntPtr 转换为 Boolean[]?

c# - .Net 中的 Excel 样式条件数字格式

c# - 我希望从 SQL 返回的结果显示在 C# 的消息框中

c# - 最后,是什么触发了捕获变量的新实例?

c# - 如何将 byte[] 数组转换为 IntPtr?