Register C# delegate to C++ callback, what does Marshal.GetFunctionPointerForDelegate do?

Viewed 4341

I used to register a delegate from C# to a callback function pointer like this (without using Marshal.GetFunctionPointerForDelegate):

C++ (Test.dll)

typedef void (__stdcall* Callback)(int num);

Callback pCallback = 0;

void __stdcall SetCallback(Callback callback)
{
    pCallback = callback;
}

void __stdcall ActionCpp()
{
    if(pCallback)
    {
        pCallback();
    }
}

C#

class Program
{
    [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void SetCallBack(Callback callback);

    [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void ActionCpp();

    public delegate void Callback();

    static void Action()
    {
        Console.WriteLine("Callback succeeded.");
    }

    static void Main(string[] args)
    {
        Callback myAction = new Callback(Action);
        SetCallback(myAction);
        ActionCpp(); //Suppose to do the same thing as Action();
    }
}

It seems that this way works fine. However, I found that we can do the same thing by using Marshal.GetFunctionPointerForDelegate and register the IntPtr of the delegate to the function pointer in C++. I would like to know what the difference is and which practice is better? (And also Why? Thanks in advance.)

Here is the C# code when using Marshal.GetFunctionPointerForDelegate. (Nothing changes in C++ code.)

C# (With Marshal.GetFunctionPointerForDelegate)

class Program
{
    [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void SetCallBack(IntPtr pCallBack); //change to type "IntPtr" this time.

    [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void ActionCpp();

    public delegate void Callback();

    static void Action()
    {
        Console.WriteLine("Callback succeeded.");
    }

    static void Main(string[] args)
    {
        Callback myAction = new Callback(Action);
        //GCHandle gcHandle = GCHandle.Alloc(myAction); <-- Is this necessary?
        IntPtr pMyAction = Marshal.GetFunctionPointerForDelegate(myAction);
        SetCallback(pMyAction);
        ActionCpp();
        //gcHandle.Free();
    }
}

I have another further but related question about whether it is necessary to use "GCHandle.Alloc" (as in my comments in the above code) to avoid any GC action as long as my Callback myAction is still alive? I am a newbie on C# and C++ callbacks, please let me know if I have naive mistakes made.

1 Answers
Related