0

I'm working with an unmanaged C library which has a callback function that gets called from an internal thread in the C library. The callback has a void * 'context' parameter.

I want to set this context to the address of a C# class instance so that I can access member fields and properties from the callback.

My understanding is that I need to pin the memory of the class to ensure the GC does not move it since the C code will take a copy of address of the instance. However, GCHandle.Alloc() states that: "An instance with nonprimitive (non-blittable) members cannot be pinned."

And sure enough code attempting to pin a class or a struct containing a field of class type fails at runtime.

How can I pass the address of a C# class instance to my C++ code and ensure that the address remains valid (i.e. is not moved by GC)?

EDIT #1 Correction: the library is a C library, not C++ as previously stated (corrected in text above). Not that it should make any difference.

This the prototype for the C callback:

void Callback(uint32_t hCamera, uint32_t dwInterruptMask, void *pvParams);

And wrapped in C#:

[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
public unsafe delegate void PHX_AcquireCallBack(uint hCamera, uint dwInterruptMask, 
    IntPtr pvParams);

pvParams is passed into the C library when the callback is installed. The library stores the pointer, but does not attempt to access it in any way. It just passes it back whenever it calls the callback.

When using the library from C++ code, I usually pass a this pointer for pvParams.

JPh
  • 536
  • 3
  • 20
  • *so that I can access member fields* - where? on C++ side? it doesn't make sens ... on C# side? then why you wana pin it? – Selvin Jan 07 '19 at 15:10
  • Could you share with us the function call ? Does this library has any documentation ? – Martin Verjans Jan 07 '19 at 15:17
  • Since the C++ side takes a copy of the address, I believe the C# instance must be pinned to ensure it it not moved by the GC. The C++ code is mine; see EDIT for further details. – JPh Jan 08 '19 at 09:28
  • you can use GCHandle.Alloc() on any class instance with normal handle type(not pinned) – Selvin Jan 09 '19 at 22:15
  • @Selvin Correct, but I believe I need to pin the memory since I'll be passing the address of the object to my C library. If you think I do not need to pin the memory, can you let me know why? – JPh Jan 10 '19 at 11:32
  • because GCHandle will have current addres of your object even if GC would move it ... and you would pass GCHandle pointer there not address of your object ... [see the example](https://learn.microsoft.com/en-US/dotnet/api/system.runtime.interopservices.gchandle?view=netframework-4.7.2) ... `TextWriter tw` on the example would be your Context object – Selvin Jan 10 '19 at 11:33
  • anyway I would change `PHX_AcquireCallBack` to static ... and I would tahe `this` from `pvParams` like in the example – Selvin Jan 10 '19 at 11:40
  • https://dotnetfiddle.net/ads8xY – Selvin Jan 10 '19 at 11:55
  • Your example works (I think) because the GCHandle is on the stack and remains valid until the callback returns. In my case, the 'context' is stored in the C library and an internal C thread calls the callback. So I'd have to make the GCHandle a member of my class or allocate it on the heap. Either way, the GCHandle could be moved by the GC, and thus the pointer held by the C lib be invalid. Or am missing something? – JPh Jan 18 '19 at 11:18

0 Answers0