In C I have a function which registers a callback and a state object and then passes the state object to the callback every time it is called, much like EventHandler<EventArgs> works in C#. I have a class in C# that registers a managed callback with the C function and currently passes IntPtr.Zero and I simply do not use it because I have not found a good and clean way of passing a managed object reference to C and getting it back to C#.
I do not want the managed object to be accessible in C, I just want to pass it to C and have it passed back (verbatim) to the managed callback every time it is called from C.
Looking for the “right” way of doing this, basically.
Here is my delegate for the callack:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.SysInt)]
internal delegate IntPtr LuaAllocator(
[In, MarshalAs(UnmanagedType.AsAny)] object args,
[In, MarshalAs(UnmanagedType.SysInt)] IntPtr ptr,
[In, MarshalAs(UnmanagedType.SysInt)] IntPtr originalSize,
[In, MarshalAs(UnmanagedType.SysInt)] IntPtr newSize);
Here is the current DllImport I’m doing:
[DllImport(DllName, EntryPoint = "lua_newstate", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.SysInt)]
public static extern IntPtr NewState(
[In, MarshalAs(UnmanagedType.FunctionPtr)] LuaAllocator allocator,
[In, MarshalAs(UnmanagedType.AsAny)] object args);
And the error I’m getting right now is:
MarshalDirectiveException was unhandled:
Cannot marshal ‘parameter #1’: AsAny cannot be used on return types, ByRef parameters, ArrayWithOffset, or parameters passed from unmanaged to managed.
This error goes away if I keep the delegate’s first argument as an IntPtr and use MarshalAs.SysInt but I then cannot reconstruct the managed object, but I do get with a value other than IntPtr.Zero which is curious and may be useful.
I have found a solution. The
HandleRefcomment pointed me toGCHandlewhich I’m already familiar with but had never done this with. By making a non-pinnedGCHandlefrom the object you want to marshal you can call the staticGCHandle.ToIntPtr(GCHandle)method to acquire anIntPtrrepresenting that particular handle. Inside the callback I calledGCHandle.FromIntPtr(IntPtr)to re-acquire the handle and then retrieved theSystem.Objectfrom theGCHandle.Targetproperty.Here’s how you set up the callback and the state object:
And here is how you utilize it in the callback: