我尝试以这种方式分配一个结构数组:
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.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, 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/