Search code examples
c#reflectioninteropclrcil

How to get .net managed method pointer by "MethodName" that can be called on native code


Precondition

The .net method that I will get its pointer is:

  • public static method
  • have no overloads
  • arguments and return value just ValueType (unsafe pointer, primitive type, unmanaged struct)

Reason

Get the method pointer so I can call in C++ program.

This works for me but I need to declare delegate for every method.

I want to get rid of doing things over and over again.

In .net side:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void UpdateDelegate(float delta);

public static void* GetUpdatePointer()
{
    var delegateInstance = = new UpdateDelegate(Update);
    var pfnUpdate = Marshal.GetFunctionPointerForDelegate(delegateInstance);
    return (void*)pfnUpdate;
}
public static Update(float delta)=>{...}

In C++ side:

typedef void (_stdcall *  FuncPtr)(float);
void foo()
{
    //just pseudo-code showing where is the pfnUpdate from.
    FuncPtr pfnUpdate = (FuncPtr)GetUpdatePointer();
    pfnUpdate(0.01f);
}

what I want

In c#, I export GetMethodPointer for my native code. It will return a function pointer to specified method, and this pointer can be invoked by native program via stdcall calling convention.

//avoid gc collect this object
static List<Delegate> KeepReference = new List<Delegate>();
public unsafe static void* GetMethodPointer(string name)
{
    System.Reflection.MethodInfo methodInfo = typeof(PhysicsMain).GetMethod(name);

    // also mark this delegate with [UnmanagedFunctionPointer(CallingConvention.StdCall)] attribute
    Type delegateType = ConstructDelegateTypeWithMethodInfo(methodInfo);

    var delegateInstance = Delegate.CreateDelegate(delegateType, methodInfo);

    KeepReference.Add(delegateInstance);
    return (void*)Marshal.GetFunctionPointerForDelegate(delegateInstance);
}

I need ConstructDelegateTypeWithMethodInfo to create a delegate with the same signature as the specified method. And mark [UnmanagedFunctionPointer(CallingConvention.StdCall)] attribute for it so that can be marshaled as a function pointer.

I think it may using IL, Reflection, even Asm to do this. Or using IL to write the whole GetMethodPointer method.


Solution

  • Finally, I got a solution these days. Firstly, I came across Expression.GetDelegateType given by this post. But it did't work for me, because Marshal.GetFunctionPointerForDelegate doesn't support generic delegate type generate by Expression.GetDelegateType. I thought there might be a clue in implementation of Expression.GetDelegateType. So, I browsed referencesource and got a internal method called MakeNewCustomDelegate. This link gives code about how to call the internal method. Things are readily solved!

    Edit: I forgot to say, the default unmanaged calling convension of a delegate is stdcall, so we don't need to mark the delegate with [UnmanagedFunctionPointer(CallingConvention.StdCall)] explicitly.