Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c#

callback

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.

like image 721
Lawrence Avatar asked May 31 '26 14:05

Lawrence


1 Answers

A callback requires the start address of the method that handles the event. So your two methods are both getting the same start address. The first method is clear since you are using the name of the method. 2nd method you have more statements that accomplishes the same as first method.

c# is managed language while c++ is un-managed. Which basically means that extra protection was added to c# to prevent PC from going blue screen. So when calling un-managed c++ from c# rules must be followed to prevent blue screen. So any pointer variables passed to a c++ method must be allocated in c# as un-managed using GCHandle.Alloc or by using other Marshal methods. Marshal.StrToPtr will also allocate the un-managed memory.

like image 98
jdweng Avatar answered Jun 02 '26 02:06

jdweng



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!