7

I want to pin a generically-typed array and get a pointer to its memory:

T[] arr = ...;
fixed (T* ptr = arr)
{
    // Use ptr here.
}

But trying to compile the above code produces this compiler error:

Cannot take the address of, get the size of, or declare a pointer to a managed type

The answers to these questions confirm that there's no way to declare a generic T* pointer. But is there a way to pin a generic T[] array and get an IntPtr to the pinned memory? (Such a pointer still has use because it could be passed to native code or cast to a pointer of known type.)

Community
  • 1
  • 1
Walt D
  • 4,491
  • 6
  • 33
  • 43
  • 3
    This question is going to get a lot of C# programmers in trouble. There is a big, big difference between IntPtr and T*. No, you cannot blindly pass the IntPtr to native code, it is likely to suffer the same fate as your C# program. But without the exception to warn you about it. Non-trivial T types need to be marshaled so their layout is well defined and member type conversions are taken care of. Marshal.StructureToPtr() does this. Using T[] in the pinvoke declaration is always optimal, both safe *and* fast when it can be. – Hans Passant Jun 12 '16 at 19:17
  • 1
    @HansPassant - Those are good points to keep in mind, and one should definitely prefer to use the standard marshaling features when possible. But in some cases, you just have no choice. For example, when calling a native API that takes a void* pointer (because the API can take data in arbitrary formats), so pinvoke marshaling won't take care of that for you. I'm personally working a lot with DirectX via the SharpDX .Net wrapper, and this comes up *a lot*. – Walt D Jun 12 '16 at 19:37
  • Also, there are cases where, for performance reasons (disclaimer: profile to identify performance bottlenecks before resorting to unsafe code like this), you want to get a pointer to an array and copy its entire contents all at once to a destination pointer. One could theoretically call StructureToPtr on each element of the array, but that's a lot more expensive than simply copying the whole array. (And StructureToPtr also creates boxing garbage; even the generic overload.) – Walt D Jun 12 '16 at 20:13

1 Answers1

7

Yes, you can use the GCHandle object to pin a generic T[] array and get an IntPtr to its memory while pinned:

T[] arr = ...;
GCHandle handle = GCHandle.Alloc(arr, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
// Use the ptr.
handle.Free();

Make sure you remember to call Free(), because otherwise the array will never become unpinned.

Walt D
  • 4,491
  • 6
  • 33
  • 43