我尝试以这种方式分配结构数组:
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 函数将数据复制到结构变量中吗?换句话说,修改结构体是否会反映结构体数组数据的修改?
当然,我想使用结构体对 IntPtr 指向的数据进行操作,而不是每次都复制数据,从而避免不安全的代码。
谢谢大家!
我能想到的有四种选择,两种仅使用“安全”代码,两种使用不安全代码。不安全的选项可能会快得多。
Safe:
-
在托管内存中分配数组,并声明 P/Invoke 函数来获取该数组。即,而不是:
[DllImport(...)]
static extern bool Foo(int count, IntPtr arrayPtr);
make it
[DllImport(...)]
static extern bool Foo(int count, NativeType[] array);
(我用过NativeType
为你的结构名称而不是T
, since T
通常在一般上下文中使用。)
据我了解,这种方法的问题在于,NativeType[]
每次调用数组都会被整理两次Foo
。它将从托管内存复制到非托管内存
调用之前的内存,然后从非托管内存复制到托管内存。不过,如果Foo
只会读取或写入数组。在这种情况下,装饰tarray
参数与[In]
(只读)或[Out]
(只写)属性。这允许运行时跳过复制步骤之一。
-
正如您现在所做的那样,在非托管内存中分配数组,并使用一系列调用Marshal.PtrToStructure
and 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:
-
在非托管内存中分配数组,并使用指针访问元素。这意味着使用该数组的所有代码都必须位于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);
-
在托管内存中分配数组,并在需要调用本机例程时固定它:
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);
}
如果您可以使用不安全代码并且关心性能,最后一个选项可能是最干净的,因为您唯一的不安全代码是调用本机例程的地方。如果性能不是问题(也许数组的大小相对较小),或者如果您不能使用不安全的代码(也许您没有完全信任),那么第一个选项可能是最干净的,但是,正如我所提到的,如果您在调用本机例程之间访问的元素数量只占数组中元素数量的一小部分,那么第二个选项会更快。
Note:
不安全操作假设您的结构是可直接传送 http://en.wikipedia.org/wiki/Blittable_types。如果没有,那么安全的日常活动是您唯一的选择。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)