2

I have a C# method that looks like this:

public delegate void VariableObserver(string variableName, object newValue);

public void ObserveVariable(string variableName, VariableObserver observer)
{
    // stuff
}

This code is embedded into a c++ application and I want to call the ObserveVariable function from C++, passing a function pointer or something equivalent as the VariableObserver delegate. The intention is then that the C# code can call the delegate and the C++ function will be invoked.

I'm used to using mono_runtime_invoke for calling C# functions, and it works great. However I can't find any useful examples of documentation of how you can pass a function pointer/delegate. Do you need to box it up like you do with some types like strings etc? If so how do you do this? is it even possible?

I found the Marshal.GetDelegateFromFunctionPointer method while searching around, and I figured I could use that in C# to get the delgate and carry on as normal, however these docs says this is now obsolete.

Edit: some more things I discovered. There's this newer Marshal function: Marshal.GetDelegateForFunctionPointer<TDelegate>(IntPtr) which looked promising, but it says explicitly "You cannot use this method with function pointers obtained through C++ or from the GetFunctionPointer method." :(

Edit2: I've tried the suggestion of just using GetDelegateForFunctionPointer, but it doesn't look like the function pointer is marshalled correctly. Take a look:

C# code:

delegate void TestObserver(int num);

public void BindObserver(IntPtr observerFuncPtr)
{
    TestObserver obs = Marshal.GetDelegateForFunctionPointer<TestObserver>(observerFuncPtr);
    obs(1337);
}

C++ code:

static void ObserverCallback(int num)
{
    printf("WE GOT A CALLBACK %d", num);
}

// inside a function I call

void* params[1];
params[0] = &ObserverCallback;

MonoObject* exception;
mono_runtime_invoke(theMonoMethod, theMonoInstance, params, &exception);

However doing this results in this error from C#:

Runtime Exception : System.NullReferenceException: Object reference not set to an instance of an object
  at (wrapper managed-to-native) System.Object:wrapper_native_8348575608244C89 (int)
  at InkGlue.GlueStory.BindObserver (System.IntPtr observerFuncPtr) [0x00006] in <78fcd644f8db43978f6f7b1655d0ab2f>:0 

Which leads me to believe that the function pointer is not successfully being passed to C#. Any info on how to do this correctly?

DavidColson
  • 8,387
  • 9
  • 37
  • 50
  • Try adding `extern “C”` to the declaration of ObserverCallback, to ensure it follows C calling conventions. – Zastai Aug 25 '18 at 17:34
  • And you might need `[UnmanagedFunctionPointer(CallingConvention.Cdecl)]` on the delegate declaration too. – Zastai Aug 25 '18 at 17:43
  • &ObserverCallback is not the correct function pointer. You are missing the function pointer type - see: https://stackoverflow.com/questions/2298242/callback-functions-in-c – awattar Aug 19 '21 at 18:43

2 Answers2

1

Easy workaround is a C shim. https://stackoverflow.com/a/31232805/179795 and other answers in that question may also help.

Behrooz
  • 1,696
  • 2
  • 32
  • 54
  • Thanks, but I only have this small use case and I feel adopting this would be too big for such a small problem. Currently considering doing it another way entirely. – DavidColson Aug 20 '18 at 22:57
0

I think Marshal.GetDelegateForFunctionPointer will work if you pass it the address of a function marked static. Pass this as an explicit parameter if necessary.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48