Pinning Array of .NET objects

2019-04-28 10:54发布

问题:

I would like to pin an array of .NET objects (including the objects) in order to allow a native function to do some processing on the objects. As far as I understood, GCHandle.Alloc() does not allow me to do this, because such an array contains references (and the objects might also contain references) which are not blittable.

Is there any other option to achieve this? I would be okay with very hack-y suggestions or ones that require Mono.

回答1:

Arrays in .NET are represented in contiguous memory. So that means that in memory, after element 0, element 1 will come directly after the previous element, and so on.

If you pin the array with GCHandle.Alloc, then that means the entire list of elements is pinned in memory as well, and you can process that in unmanaged code.

However, as you've mentioned, it only makes sense if the type is a blittable type (technically, this is not true, it's if the type is able to be marshaled to unmanaged code, although there's a lot of overlap here between the blittable primary types and stuff that the P/Invoke/COM Interop layers handle automatically).

So if you have an array of value types, you can call GCHandle.Alloc and it will pin the array for you. However, the P/Invoke layer already does this for you, so you shouldn't be concerned with this.

If your array is full of references, then marshalling it to unamanged code doesn't make sense anyways; even if you pin every reference, the unmanaged code wouldn't know what to do with that reference in memory, as the type system doesn't support the .NET type that the reference is pointing to in memory.

If the class in .NET is really a wrapper/.NET representation of a native structure, then you're better off creating an array of that structure in .NET, copying all the data into it, and then sending it to your native code.

Or, you could write your class in C++/cli to facilitate the access of the .NET members in native code.



回答2:

You could take a look at Marshal.AllocHGlobal and Marshal.WriteIntPtr. This is the official way that framework provides to work with unmanaged memory. Not sure if it will be helpful though.

Edit

See also https://stackoverflow.com/a/878147/301525