It's a bit odd asking this question, because I have code that seems like it shouldn't work, but it does, and although I'm not complaining, I'd like to confirm why? LOL
Simply, I have a C++ native DLL (no CLR/managed support at all) that takes a call-back from C# code. The native side stores an stdcall callback function, which is supplied by the C# side. I always thought the callback METHOD (in C#) had to be static, but non-static and lambda expression BOTH work JUST FINE!? How is the "this" pointer being marshaled from native code? I always thought that native code only stores non-instance function pointers?
Now, I did find an article where some guy emitted IL code to "bridge" between native and non-static managed callbacks. I also noticed this depreciated method: "Marshal.GetUnmanagedThunkForManagedMethodPtr()". The method is no longer supported, which I'm assuming means it is built in?
Question summary:
Is thunking now built natively into .NET by emitting IL code? If so, at what version of .NET did this become natively supported?
Is implicit "thunking" supported in Mono as well?
When the IL is emitted for the managed callbacks, what happens when the instance the thunk refers to is deleted? Is the IL removed, or might this lead to a memory "leak" so to speak?
Thanks.
The interop marshaller simply marshals a delegate. The delegate can be either an class delegate (no
this
) or an instance delegate (has athis
). From the standpoint of C#, it's just calling a delegate. i.e. the same semantics used to managethis
with an instance delegate (e.g. the decoupling of the class instance) is effectively used.Clearly there's more going on under the covers with other things (like pinning, etc.)--but they're generally unrelated to what you've asked.
No IL in involved in the thunk, it happens by emitting native code -- a trampoline that rearranges arguments to meet .NET's calling convention, including the
this
pointer which is saved in case of closed delegates, and then performs a tail call to the .NET method itself.This is called "reverse p/invoke", that should make it easy to look for in the Mono docs.
When the delegate is garbage collected, memory used by the trampoline is also freed. So you need to keep the delegate alive as long as the native code has a pointer to the trampoline.