Marshal.GetDelegateForFunctionPointer on Android

mbelewmbelew Member ✭✭
edited April 2018 in Xamarin.Android

Hi all,
I'm trying to use GetDelegateForFunctionPointer for a c++ native function. I have this working in Windows, but on Android, the C++ call stack appears to be garbage. I'm sure it has to do with how mono is handling the native argument stack. My guess is that JNICALL (Java) is different than __stdcall (Windows), but GetDelegateForFunctionPointer requires the __stdcall format. Please take a look at SomeFunction() below; it's the call getting garbage argument stack.

C++ (Android NDK)

// function called by C# through GetDelegateForFunctionPointer
extern "C" JNIEXPORT
int JNICALL SomeFunction(int num, float flt, char *msg)
{
    // Windows shows correct arguments, but Android shows garbage arguments
    m_strdup_printf ("Some Function data '%s'.", msg);

    return 0;
}


// calling code using embedded Mono
...
    MonoMethodDesc* sayHelloDesc = mono_method_desc_new("Class1:ManagedMethodSayHello(string,int,intptr)", false);
    MonoMethod* sayHelloMeth     = mono_method_desc_search_in_class(sayHelloDesc, pClass);

    if (!sayHelloMeth)
    {
        result = -1;
        return;
    }

    char *arg = "Hello World";
    int num = 0x64;
    int(JNICALL *func)(int, float, char *) = &SomeFunction;

    void * newArgs[3];

    newArgs[0] = mono_string_new(mono_domain_get(), arg);
    newArgs[1] = #
    newArgs[2] = &func;

    exc = 0;

    ret = mono_runtime_invoke(sayHelloMeth, class1Instance, newArgs, &exc);
...


c#

    // delegate pattern that matches 'SomeFunction' argument list
    delegate int CFuncDelegate(int num, float flt, string msg);

    public interface InterfaceClass1
    {
        IntPtr ManagedMethodSayHello(string arg, int num, IntPtr func);
    }

    public class Class1 : InterfaceClass1 
    {
        Class1()
        {
        }

        public IntPtr ManagedMethodSayHello(string arg, int num, IntPtr func)
        {
            CFuncDelegate myfunc = (CFuncDelegate)Marshal.GetDelegateForFunctionPointer(func, typeof(CFuncDelegate));

            // make the call (Mono handles Marshaling of the string argument in Windows implementation)
            int res = myfunc(55, 3.14f, "Did you see what I did here?");

            return func;
        }

Incidentally, here's the same working method from Windows

int __stdcall SomeFunction(int num, float flt, char *msg)
{
    COutputWnd::Instance->AppendBuild(CA2W(msg));

    return 0;
}


...
    // int ManagedMethodSayHello(string arg, int num, IntPtr func)
    MonoMethodDesc* sayHelloDesc = mono_method_desc_new("Class1:ManagedMethodSayHello(string,int,intptr)", FALSE);
    MonoMethod* sayHelloMeth     = mono_method_desc_search_in_class(sayHelloDesc, pClass);

    if (!sayHelloMeth)
    {
        result = -1;
        return;
    }

    char *arg = "Hello World";
    int num = 0x64;
    int(__stdcall *func)(int num, float flt, char *) = &SomeFunction;

    void * newArgs[3];

    newArgs[0] = mono_string_new(mono_domain_get(), arg);
    newArgs[1] = #
    newArgs[2] = &func;

    exc = 0;
    ret = mono_runtime_invoke(sayHelloMeth, class1Instance, newArgs, &exc);

...
Sign In or Register to comment.