Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a delegate or function pointer from C# to C++ and call it there using InternalCall

I have the following setup in C#:

public delegate void CallbackDelegate(string message);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern void setCallback(CallbackDelegate aCallback);

public void testCallbacks()
{
    System.Console.Write("Registering C# callback...\n");
    setCallback(callback01);
}

public void callback01(string message)
{
    System.Console.Write("callback 01 called: " + message + "\n");
}

And this in C++ (the function is registered correctly via mono_add_internal_call ):

typedef void (*CallbackFunction)(const char*);
void setCallback(MonoDelegate* delegate)
{
    // How to convert the MonoDelegate to a proper function pointer?
    // So that I can call it like func("test");
}

The C++-function is called and something is passed to the delegate variable. But what now?

I looked around and found the function "mono_delegate_to_ftnptr" mentioned a few times, and from those examples it seems to be exactly what I need.
However, this function simply does not seem to exist in my distribution of mono (4.6), so I can only guess it does not exist any more.

I also found a few examples of how to do this with PInvoke. Which is something I do not want to use - since InternalCall is much faster for my purpose.
Of course, if PInvoke would be the only way, so be it, but I doubt that.

In the end, I am really at a loss at how to proceed from here.

like image 422
TheSHEEEP Avatar asked Sep 30 '16 11:09

TheSHEEEP


People also ask

How do you pass a function pointer?

Pass-by-pointer means to pass a pointer argument in the calling function to the corresponding formal parameter of the called function. The called function can modify the value of the variable to which the pointer argument points. When you use pass-by-pointer, a copy of the pointer is passed to the function.

Is a delegate a function pointer?

A delegate is a type-safe function pointer that can reference a method that has the same signature as that of the delegate. You can take advantage of delegates in C# to implement events and call-back methods. A multicast delegate is one that can point to one or more methods that have identical signatures.

Can we pass delegate as parameter?

C# Delegate. In C#, we can also pass a method as a parameter to a different method using a delegate. We use the delegate keyword to define a delegate. Here, Name is the name of the delegate and it is taking parameter.

How do you call a Func delegate method in C#?

Func<int, int, int> Add = Sum; This Func delegate takes two parameters and returns a single value. In the following example, we use a delegate with three input parameters. int Sum(int x, int y, int z) { return x + y + z; } Func<int, int, int, int> add = Sum; int res = add(150, 20, 30); Console.


2 Answers

After some more hours of digging I finally found a (the?) solution.
Basically, what works for the PInvoke approach works here as well, you can pass a function pointer instead of a delegate from C# to C(++).
I'd prefer a solution where you can pass a delegate directly, but you can always add some wrapper code in C# to at least make it look like that.

Solution:

C#:

public delegate void CallbackDelegate(string message);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern void setCallback(IntPtr aCallback);

private CallbackDelegate del; 
public void testCallbacks()
{
    System.Console.Write("Registering C# callback...\n");
    del = new CallbackDelegate(callback01);
    setCallback(Marshal.GetFunctionPointerForDelegate(del));

    System.Console.Write("Calling passed C++ callback...\n");
}

public void callback01(string message)
{
    System.Console.Write("callback 01 called. Message: " + message + "\n");
}

C++:

typedef void (*CallbackFunction)(MonoString*);
void setCallback(CallbackFunction delegate)
{
    std::cout << &delegate << std::endl;
    delegate(mono_string_new(mono_domain_get(), "Test string set in C++"));
}

Watch out, though: You need to keep the delegate around in C# somehow (which is why I assigned it to "del") or it will be caught by the GC and your callback will become invalid.
It makes sense, of course, but I feel this is easy to forget in this case.

like image 160
TheSHEEEP Avatar answered Sep 30 '22 13:09

TheSHEEEP


You can pass a function pointer as a parameter in C++ to C# using intptr_t.

MSDN isn't accurate, below code works:

In C++:

static void func(int param)
{
    //...
}  
    
void other_func()
{
    ptr->SetCallback( reinterpret_cast<intptr_t>(func));
}

In C#:

public static mydelegatetype somefunc = null;
    
public void SetCallback(IntPtr function_pointer)
{
    somefunc = (mydelegatetype)
    Marshal.GetDelegateForFunctionPointer(function_pointer, typeof(mydelegatetype));
}
like image 29
sailfish009 Avatar answered Sep 30 '22 13:09

sailfish009