Search code examples
c#c++dllmonomarshalling

Embedded mono: return / copy an array from C# DLL to C++


I have a C++ application that is supposed to use certain functions from a DLL which is written in C# and compiled using mono. I already figured out how to make those C# functions run from my C++ code, but still having trouble understanding how to refer to the C# result which would normally be an array of a known size.

My C++ code looks like this:

MonoDomain* domain = mono_jit_init("CSharpDLLname.dll");
MonoAssembly* assembly = mono_domain_assembly_open(domain, "CSharpDLLname.dll");
MonoImage* image = mono_assembly_get_image(assembly);
MonoClass* klass = mono_class_from_name(image, "MyNamespace", "MyClass");
MonoObject* object = mono_object_new(domain, klass);
// call ctor
mono_runtime_object_init(object);

MonoMethodDesc* mdesc = mono_method_desc_new(":test(single[])", false);
MonoMethod* method = mono_method_desc_search_in_class(mdesc, klass);

void* args[1];
args[0] = &something;

// call function with provided args
mono_runtime_invoke(method, object, args, NULL);

// shutdown the runtime
mono_jit_cleanup (domain);

This is the type of function I'm calling within the C# DLL:

public static float[] tester(float[] arr)
{
    // ... do things to arr
    // create an array of known size
    float[] barr = new float[3];
    barr[0] = 1;
    barr[1] = 2;
    barr[2] = 3;
    // return it as result
    return barr;
}

The question is how do I obtain the pointer or copy of the barr from my C++ code?

I tried using

MonoObject* returnObj = mono_runtime_invoke(method, object, args, NULL);
float* result = (float*)mono_object_unbox (returnObj);

But that results in

Assertion at object.c:6493, condition `obj->vtable->klass->valuetype' not met. ... Got a SIGABRT while executing native code. This usually indicates a fatal error in the mono runtime or one of the native libraries used by your application.

If I return a single value like public static float tester(), then it works and I'm able to read the result. Now I want to be able to read an array.

A simple C+ usage example of a C# function that returns an array would be great. Or, if mono_runtime_invoke() and mono_object_unbox() is not the right approach (I'm beginner with C# and mono), would be nice to know how to do it correctly - again, basic example is appreciated.


Solution

  • I could not find an answer on how to return or copy the whole array, but figured out the other way. Since obtaining of the blittable types works, instead of a function which returns the whole array, I used to get each element of the array. For example:

    The C# code:

    private float[] m_array; // the array we want to copy to our C++ code 
    private int m_numElements; // length of the array
    
    public static int getLength()
    {
        return m_numElements; 
    }
    
    public static float[] getArray() // this will not work
    {
        return m_array;
    }
    
    public static float getArrayElement(int index) // we will use this instead!
    {
        return m_array[index];
    }
    

    The usage within C++ code:

    MonoObject* lengthObj = mono_runtime_invoke(methodGetLength, object, NULL, NULL);
    int length = *(int*)mono_object_unbox(lengthObj);
    
    // now allocate the array where you will cope the result to
    std::vector<float> array; // or could also be float[length]
    array.resize(length);
    for (int i=0; i<length; ++i){
        void* args[1];
        args[0] = &i;
        // obtain the next element with index i
        MonoObject* elementObj = mono_runtime_invoke(methodGetElement, object, args, NULL);
        // copy the result to our array
        array[i] = *(float*)mono_object_unbox(elementObj);
    }