I am trying to invoke a simple C# class method from C, using embedded mono (as described here). I can invoke the method, but the C# function receives 0 as the argument, instead of the number I pass in. The C# function returns a result and the C code is seeing the correct result - I just can't pass arguments in. What am I doing wrong?
The C# assembly (MonoSide.cs) is:
using System;
public class MainEntryPoint
{
static public void Main(string[] args)
{
}
}
namespace Fibonacci
{
public class Fibonacci
{
public long FibonacciNumber(long entryNumber)
{
Console.Write(string.Format("(inside C#) FibonacciNumber({0})", entryNumber));
var sqrt5 = Math.Sqrt(5);
var phi = (1 + sqrt5) / 2;
var exp = Math.Pow(phi, entryNumber);
var sign = ((entryNumber & 1) == 0) ? -1 : 1;
var entry = (exp + sign / exp) / sqrt5;
Console.WriteLine(string.Format(" = {0}.", entry));
return (long) entry;
}
}
}
Here is the C code:
#include <stdio.h>
#include <stdlib.h>
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
int main(int argc, char **argv)
{
long long entryNumber = (argc > 1) ? atoi(argv[1]) : 10;
// For brevity, null checks after each mono call are omitted.
MonoDomain *domain = mono_jit_init("MainEntryPoint");
MonoAssembly *monoAssembly = mono_domain_assembly_open(domain, "MonoSide.exe");
char *monoArgs[] = {"Mono"};
mono_jit_exec (domain, monoAssembly, 1, monoArgs);
MonoImage * monoImage = mono_assembly_get_image (monoAssembly);
MonoClass * monoClass = mono_class_from_name (monoImage, "Fibonacci", "Fibonacci");
MonoMethod *monoMethod = mono_class_get_method_from_name(monoClass, "FibonacciNumber", 1);
// Invoking method via thunk.
typedef long long (*FibonacciNumber) (long long *);
FibonacciNumber fibonacciNumber = mono_method_get_unmanaged_thunk (monoMethod);
printf("Calling C# thunk function FibonacciNumber(%I64u)...\n", entryNumber);
long long number = fibonacciNumber(&entryNumber);
printf("Fibonacci number %I64u = %I64u\n", entryNumber, number);
mono_jit_cleanup (domain);
return 0;
}
I am compiling it with Dev-Cpp using this makefile:
test.exe: CSide.c MonoSide.exe
gcc CSide.c -o test.exe -m32 -mms-bitfields -IC:/Progra~2/Mono/Include/mono-2.0 -LC:/Progra~2/Mono/lib -L/Embedded -lmono-2.0 -lmono
MonoSide.exe: MonoSide.cs
mcs MonoSide.cs
The output is:
Calling C# thunk function FibonacciNumber(10)...
(inside C#) FibonacciNumber(0) = 0.
Fibonacci number 10 = 0
(Why these functions? This is just a sample, can-I-get-this-to-work program and not my final goal.)
Edit:
It works if I pass the function argument as a pointer in the C code. The C# receives it correctly. The above code has been modified from:
typedef long long (*FibonacciNumber) (long long);
...
long long number = fibonacciNumber(entryNumber);
to:
typedef long long (*FibonacciNumber) (long long *);
...
long long number = fibonacciNumber(&entryNumber);
To me, this means that the safest way to pass anything more complicated between C and C# is via buffers, with matching serializers and deserializers in C and C#.