struct T { int a; int b; } data = Marshal.AllocHGlobal(count*Marshal.SizeOf(typeof(T)); ...
我想访问分配的数据“绑定”结构到分配的数组中的每个元素
与AllocHGlobal ……这样的事情
T v; v = (T)Marshal.PtrToStructure(data+1,typeof(T));
但我没有找到任何方便的方法…为什么IntPtr缺乏算术?我该如何以“安全”的方式解决这个问题?
有人可以确认PtrToStructure函数将数据复制到struct变量中吗?换句话说,修改结构体是否反映了结构数组数据中的修改?
当然,我想对使用struct的IntPtr指向的数据进行操作,而不是每次都复制数据,避免使用不安全的代码.
谢谢大家!
解决方法
安全:
>在托管内存中分配您的数组,并声明您的P / Invoke函数以获取该数组.即,而不是:
[DllImport(...)] static extern bool Foo(int count,IntPtr arrayPtr);
做了
[DllImport(...)] static extern bool Foo(int count,NativeType[] array);
(我已经使用NativeType作为结构名而不是T,因为T通常用在通用上下文中.)
这种方法的问题在于,据我所知,NativeType []数组将在每次调用Foo时被封送两次.它将从托管内存复制到非托管内存
调用之前的内存,然后从非托管内存复制到托管内存.但是,如果Foo只读取或写入数组,则可以进行改进.在这种情况下,使用[In](只读)或[Out](只写)属性修饰tarray参数.这允许运行时跳过其中一个复制步骤.
>正如您现在所做的那样,在非托管内存中分配数组,并使用一堆对Marshal.PtrToStructure和Marshal.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,// you can change ToInt64() to ToInt32(). Marshal.StructureToPtr(value,(IntPtr)(arrayPtr.ToInt64() + index * Marshal.SizeOf(typeof(T)),false); }
不安全:
>在非托管内存中分配数组,并使用指针访问元素.这意味着使用该数组的所有代码必须位于不安全的块中.
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);
>在托管内存中分配数组,并在需要调用本机例程时将其固定:
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); }
如果您可以使用不安全的代码并关注性能,那么最后一个选项可能是最干净的,因为您唯一不安全的代码是您调用本机例程的地方.如果性能不是问题(可能是因为数组的大小相对较小),或者如果你不能使用不安全的代码(也许你没有完全信任),那么第一个选项可能是最干净的,但是,正如我所提到的,如果在调用本机例程之间访问的元素数量只是数组中元素数量的一小部分,那么第二个选项会更快.
注意:
不安全的操作假设你的结构是blittable.如果没有,那么安全例程是你唯一的选择.