1. 程式人生 > >使用mono構建c#指令碼執行環境

使用mono構建c#指令碼執行環境

Unity引擎使用mono作為指令碼的執行環境,本文主要使用mono的internal call來進行c++與c#的互操作

1.資料格式

進行互操作是需要進行資料傳遞,這裡主要分為三類,原生型別,值型別,以及引用型別

1.1 原生型別

常用如下
c++ c# mono
int int int
float float int
char* string MonoString*

c#的string和MonoString*會在使用是自動轉換,所以需要進行MonoString* 與char*的轉換,這裡直接使用了stl的std::string.

std::string MonoStringToString(MonoString* monoString)
{
    char* pStr = mono_string_to_utf8(monoString);
    std::string str = pStr;
    mono_free(pStr);
    return str;
}

MonoString* StringToMonoString(std::string str)
{
    MonoDomain* domain = mono_domain_get();

    MonoString* monoString = mono_string_new(domain, str.c_str());

    return
monoString; }

陣列物件分別是c++的陣列,c#的陣列,以及Mono的MnonoArray*
轉換如下

c++

template<class T>
MonoArray* VectorToMonoArray(std::vector<T*> vec, MonoClass* monoClass)
{
    MonoArray* pMonoArr = NULL;
    //MonoClass* pMonoClass = mono_object_get_class(T::getTypeNameStatic());
    MonoDomain* pDomain = mono_domain_get();
    MonoType* t = mono_class_get_type(monoClass);
    size_t size = vec.size();

    // - create a ubyte array
pMonoArr = mono_array_new(pDomain, monoClass, size); if (NULL == pMonoArr) { return NULL; } // - convert string into mono string and add them to a mono byte array for (uint32 ii = 0; ii<size; ii++) { MonoObject* tp = vec[ii]->getMonoObject(); //這裡已經繫結好了c#物件,具體程式碼往下看 mono_array_set(pMonoArr, MonoObject*, ii, tp); } return pMonoArr; } template<class T> std::vector<T*> MonoArrayToVector(MonoArray* monoArray) { MonoObject* pStr = NULL; size_t size = mono_array_length(monoArray); std::vector<T*> arr; for (uint32 ii = 0; ii<size; ii++) { pStr = mono_array_get(monoArray, MonoObject*, ii); T* tmp = convert<T>(pStr); arr.push_back(tmp); } return arr; }

1.2 值型別

值型別是指在c#中使用struct進行定義的型別,和enum定義的型別。對於值型別,只需要在c++和c#中分別定義相應的成員變數即可,例如
c++

class Vector2
{
public:
    float x,y;
};

c#

struct Vector2
{
public:
    float x,y;
}

又或者
c++

enum Direction
{
    front,back,left,right
};

c#

enum Direction
{
    front,back,left,right
}

注意,這裡的順序需要保持一致。這裡要注意,自己定義的值型別不能作為繫結函式的返回值,否則會出現異常的錯誤。所以為了正確地從c++中獲取值,需要使用引用。例子如下
c++

void ICall_ClassA_getPosition(MonoObject* self,Vector2& pos)
{
    ...
}

c#

private extern static void ICall_ClassA_getPosition(ClassA self, out Vector2 pos);

1.3 引用型別

在mono中統一轉換為MonoObject*

2.環境初始化,繫結,呼叫,清理

    // 設定搜尋路徑,主要用於搜尋mscorlib.dll
    std::string MonoPath = "C:/Program Files (x86)/Mono";
    std::string ManagedLibraryPath = "E:/TestAllRuntime/TestAllRuntime.dll";

    std::string monoPath = MonoPath;
    std::string monoLibPath = monoPath + "/lib";
    std::string monoEtcPath = monoPath + "/etc";

    mono_set_dirs(monoLibPath.c_str(),monoEtcPath.c_str());
    // dll所在的位置
    const char* managed_binary_path = ManagedLibraryPath.c_str();

    //獲取應用域               
    domain = mono_jit_init("TestAllRuntime");  // TestAllRuntime可以隨意取
    mono_config_parse(NULL);

    //載入程式集ManagedLibrary.dll
    MonoAssembly* assembly = mono_domain_assembly_open(domain, managed_binary_path);
    image = mono_assembly_get_image(assembly);

    // 註冊函式,這裡是以後主要新增的地方 ,所有的函式都是通過該方法繫結的
    // TestAllRuntime 是c#中ClassA的namespace
    // ClassA 是 類的名稱
    // ClassA_bind 是c#中需要繫結的方法
    // &ClassA_bind 的ClassA_bind 是c++中需要繫結的方法
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_bind",
    reinterpret_cast<void*>(&ClassA_bind)); 

    MonoClass* main_class = mono_class_from_name(image, "TestAllRuntime", "Main");

    const bool include_namespace = true; //是否包含名稱空間
    MonoMethodDesc* managed_method_desc = mono_method_desc_new("TestAllRuntime.Main:main()", include_namespace);
    MonoMethod* managed_method = mono_method_desc_search_in_class(managed_method_desc, main_class);
    mono_method_desc_free(managed_method_desc);

    //呼叫剛才獲取的TestAllRuntime.Main:main(),第二個引數為NULL,表示呼叫靜態函式。如果函式不是靜態的,那麼就需要傳相應的MonoObject
    mono_runtime_invoke(managed_method, NULL, NULL, NULL);

    //清理mono環境
    mono_jit_cleanup(domain);

主要的執行流程如上述程式碼所示

3.實現指令碼功能

為了實現指令碼功能,需要在c++中持有c#物件的handle,再通過handle就可以獲得相應的c#物件。在c#中也需要持有c++物件的pointer。為了更快地從c#物件中獲取c++物件的指標,可以通過記憶體地址偏移來獲得,具體實現如下。這裡參考的是Genesis3d引擎中如何進行繫結的。程式碼如下

c#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace TestAllRuntime
{
    [StructLayout(LayoutKind.Sequential)]
    public abstract class Base
    {
        private IntPtr cppPointer; //會在c++中直接通過偏移獲取該欄位
    }
}

這裡先定義一個Base的虛基類,並使用[StructLayout(LayoutKind.Sequential)]來保證IntPtr在記憶體中的位置,具體的解釋可以百度或谷歌。其他所有的類都繼承Base。
在c++中就可以使用如下程式碼獲取c#儲存的 c++ pointer
c++

struct DataOnHead
{
    void* _cppObjectPtr;
};

static const int MonoObjectMemoryOffset = sizeof(void*) * 2;

DataOnHead* getDataOnHead(MonoObject* monoObject)
{
    return reinterpret_cast<DataOnHead*> (((char*)monoObject) + MonoObjectMemoryOffset);
}

template<class T>
T* getCppFromMono(MonoObject* monoObject)
{
    return static_cast<T*>(getDataOnHead(monoObject)->_cppObjectPtr);
}

void bindCppToMono(void* cppPtr, MonoObject* monoObject)
{
    getDataOnHead(monoObject)->_cppObjectPtr = cppPtr;
}

template<class T>
void bindMonoToCpp(MonoObject* monoObject, T* cppPtr)
{
    cppPtr->setMonoObject(monoObject);
}

template<class T>
void bindCppAndMono(MonoObject* monoObject, T* cppPtr)
{
    bindCppToMono(cppPtr, monoObject);
    bindMonoToCpp(monoObject, cppPtr);
}

這裡MonoObjectMemoryOffset為什麼是sizeof(void*)*2 可以從原始碼中或者api文件中看到

typedef struct {
    MonoVTable *vtable;
    MonoThreadsSync *synchronisation;
} MonoObject;

現在已經可以方便的獲得c#的cppPointer了,那麼同樣,在c++中可以定義變數和函式來儲存c#物件的handle,程式碼如下
c++

typedef unsigned int uint32;

const uint32 c_iInvalidMonoRefID = 0;

class ScriptDetail
{
public:
    /// constructor
    ScriptDetail()
        : m_iMonoRefID(c_iInvalidMonoRefID)
        //, m_pScriptObj( NULL )
    {}
    ScriptDetail::~ScriptDetail()
    {
        if (c_iInvalidMonoRefID != m_iMonoRefID)
        {
            m_iMonoRefID = c_iInvalidMonoRefID;
        }
    }

    void ScriptDetail::SetMonoObject(MonoObject* pObj)
    {
        if (NULL != pObj)
        {
            if (m_iMonoRefID != c_iInvalidMonoRefID)
            {
                mono_gchandle_free(m_iMonoRefID);
                m_iMonoRefID = c_iInvalidMonoRefID;
            }

            // - the second param should be 0, means this handle will not resurrection when invkoing finalisers.
            m_iMonoRefID = mono_gchandle_new_weakref(pObj, 0);
        }
        else
        {
            // - must be set before
            std::cout << "ScriptDetail::SetMonoObject,Deadly error!this prarm can't be null!\n" << std::endl;
        }
    }
    /// Get m_pScriptObj
    MonoObject* GetMonoObject(void);
    /// to see if this cpp object is binding a mono object
    bool IsBindMonoObject(void);
private:
    // - do not allow copy
    ScriptDetail(const ScriptDetail&);
    ScriptDetail& operator=(const ScriptDetail&);
    // - private data 
    uint32 m_iMonoRefID;
};

// - inline function implement ---
//------------------------------------------------------------------------
inline MonoObject* ScriptDetail::GetMonoObject(void)
{
    return mono_gchandle_get_target(m_iMonoRefID);
}
inline bool ScriptDetail::IsBindMonoObject(void)
{
    if(c_iInvalidMonoRefID  == m_iMonoRefID)
    {
        return false;
    }
    return (NULL != mono_gchandle_get_target(m_iMonoRefID));
}

// - bind
#define __ScriptBind \
public:\
    void setMonoObject( MonoObject* pObj )  { this->m_ScriptDetail.SetMonoObject( pObj );  }\
    MonoObject* getMonoObject( void )       { return this->m_ScriptDetail.GetMonoObject(); }\
    bool isBindMonoObject( void )           { return this->m_ScriptDetail.IsBindMonoObject(); }\
private:\
    ScriptDetail m_ScriptDetail;

class ClassA
{
    __ScriptBind; 

    ...
};

在類的定義中新增__ScriptBind就可以了,程式碼也很簡單。m_ScriptDetail中初始化時為0,而正確的handle一定大於0,所以根據它來判斷是否已經綁定了相應的c#物件。

主要的思想就是這樣,剩下的就是定義相應的類,並把需要繫結的函式從類中提出來,再mono_add_internal_call,
然後在c#中定義相應的類和相應的函式。
例如
c++

void ICall_ClassA_createCpp(MonoObject* obj)
{
    ClassA* t = new ClassA();
    bindCppAndMono<ClassA>(obj,t);  //繫結
}

void ICall_ClassA_setValue(MonoObject* obj,int value)
{
    ClassA* tmp = getCppFromMono<ClassA>(obj);
    tmp->setValue(value);
}
mono_add_internal_call("Test.ClassA::ICall_ClassA_setValue", &ICall_ClassA_setValue);
mono_add_internal_call("Test.ClassA::ICall_ClassA_createCpp", &ICall_ClassA_createCpp);

c#

using System.Runtime.CompilerServices;

class ClassA : Base
{
    public ClassA()
    {
        ICall_ClassA_createCpp(this);
    }

    public void setValue(int value)
    {
        ICall_ClassA_setValue(this,value);
    }

    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    private extern static void ICall_ClassA_createCpp(ClassA self);

    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    private extern static void ICall_ClassA_setValue(ClassA self, int value)
}

注意:新增[MethodImplAttribute(MethodImplOptions.InternalCall)] ,並注意 extern static 。
這裡使用static的目的是更直觀。

注意:為了在c#物件初始化的時候就初始化它的cppPointer,需要在它的建構函式中呼叫相應的函式來初始化

完整程式碼如下
ClassA.h

#pragma once

#include <string>

#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/class.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/mono-config.h>

#include <iostream>
#include <vector>

typedef unsigned int uint32;

const uint32 c_iInvalidMonoRefID = 0;

class ScriptDetail
{
public:
    /// constructor
    ScriptDetail()
        : m_iMonoRefID(c_iInvalidMonoRefID)
    {}
    ScriptDetail::~ScriptDetail()
    {
        if (c_iInvalidMonoRefID != m_iMonoRefID)
        {
            m_iMonoRefID = c_iInvalidMonoRefID;
        }
    }

    void ScriptDetail::SetMonoObject(MonoObject* pObj)
    {
        if (NULL != pObj)
        {
            if (m_iMonoRefID != c_iInvalidMonoRefID)
            {
                mono_gchandle_free(m_iMonoRefID);
                m_iMonoRefID = c_iInvalidMonoRefID;
            }

            // - the second param should be 0, means this handle will not resurrection when invkoing finalisers.
            m_iMonoRefID = mono_gchandle_new_weakref(pObj, 0);
        }
        else
        {
            // - must be set before
            std::cout << "ScriptDetail::SetMonoObject,Deadly error!this prarm can't be null!\n" << std::endl;
        }
    }
    /// Get m_pScriptObj
    MonoObject* GetMonoObject(void);
    /// to see if this cpp object is binding a mono object
    bool IsBindMonoObject(void);
private:
    // - do not allow copy
    ScriptDetail(const ScriptDetail&);
    ScriptDetail& operator=(const ScriptDetail&);
    // - private data 
    uint32 m_iMonoRefID;
};

// - inline function implement ---
//------------------------------------------------------------------------
inline MonoObject* ScriptDetail::GetMonoObject(void)
{
    return mono_gchandle_get_target(m_iMonoRefID);
}
inline bool ScriptDetail::IsBindMonoObject(void)
{
    return (NULL != mono_gchandle_get_target(m_iMonoRefID));
}

// - bind
#define __ScriptBind \
public:\
    void setMonoObject( MonoObject* pObj )  { this->m_ScriptDetail.SetMonoObject( pObj );  }\
    MonoObject* getMonoObject( void )       { return this->m_ScriptDetail.GetMonoObject(); }\
    bool isBindMonoObject( void )           { return this->m_ScriptDetail.IsBindMonoObject(); }\
private:\
    ScriptDetail m_ScriptDetail;


class ClassB;
class ClassC;
class ClassD;

enum Direction
{
    right, front, left, back
};

class ClassA
{
    __ScriptBind; 
public:
    void ClassA::setInt(int intValue)
{
    this->intValue = intValue;
}

int ClassA::getInt()
{
    return intValue;
}

void ClassA::setFloat(float floatValue)
{
    this->floatValue = floatValue;
}

float ClassA::getFloat()
{
    return floatValue;
}

void ClassA::setString(std::string stringValue)
{
    this->stringValue = stringValue;
}

std::string ClassA::getString()
{
    return stringValue;
}

    void setDirection(Direction dir)
    {
        this->dir = dir;
    }

    Direction getDirection()
    {
        return dir;
    }

    std::vector<ClassB*> getData()
    {
        return data;
    }

    void setData(std::vector<ClassB*> data)
    {
        this->data = data;
    }
private:
    int intValue;
    float floatValue;
    std::string stringValue;
    Direction dir;

    std::vector<ClassB*> data;
};

class ClassB
{
    __ScriptBind
public:
    int getValue()
    {
        return value;
    }
    void setValue(int value)
    {
        this->value = value;
    }
private:
    int value;
};

class ClassC : public ClassB
{
public:
    float getFloat()
    {
        return Float;
    }

    void setFloat(float value)
    {
        this->Float = value;
    }

private:
    float Float;
};

class ClassD : public ClassB
{
public:
    float getFF()
    {
        return ff;
    }

    void setFF(float ff)
    {
        this->ff = ff;
    }
private:
    float ff;
};

main.cpp


#include "ClassA.h"

#include <iostream>

#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/class.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/mono-config.h>

using namespace std;

MonoDomain* domain;

struct DataOnHead
{
    void* _cppObjectPtr;
};

static const int MonoObjectMemoryOffset = sizeof(void*) * 2;

DataOnHead* getDataOnHead(MonoObject* monoObject)
{
    return reinterpret_cast<DataOnHead*> (((char*)monoObject) + MonoObjectMemoryOffset);
}

void bindCppToMono(void* cppPtr, MonoObject* monoObject)
{
    getDataOnHead(monoObject)->_cppObjectPtr = cppPtr;
}

template<class T>
void bindMonoToCpp(MonoObject* monoObject, T* cppPtr)
{
    cppPtr->setMonoObject(monoObject);
}

template<class T>
void bindCppAndMono(MonoObject* monoObject, T* cppPtr)
{
    bindCppToMono(cppPtr, monoObject);
    bindMonoToCpp(monoObject, cppPtr);
}

template<class T>
T* getCppFromMono(MonoObject* monoObject)
{
    return static_cast<T*>(getDataOnHead(monoObject)->_cppObjectPtr);
}

template<class T>
MonoArray* VectorToMonoArray(std::vector<T*> vec, MonoClass* monoClass)
{
    MonoArray* pMonoArr = NULL;
    //MonoClass* pMonoClass = mono_object_get_class(T::getTypeNameStatic());
    MonoDomain* pDomain = mono_domain_get();
    MonoType* t = mono_class_get_type(monoClass);
    size_t size = vec.size();

    // - create a ubyte array
    pMonoArr = mono_array_new(pDomain, monoClass, size);
    if (NULL == pMonoArr)
    {
        return NULL;
    }
    // - convert string into mono string and add them to a mono byte array
    for (uint32 ii = 0; ii<size; ii++)
    {
        MonoObject* tp = vec[ii]->getMonoObject();

        mono_array_set(pMonoArr, MonoObject*, ii, tp);
    }

    return pMonoArr;
}

template<class T>
std::vector<T*> MonoArrayToVector(MonoArray* monoArray)
{

    MonoObject* pStr = NULL;
    size_t size = mono_array_length(monoArray);
    std::vector<T*> arr;
    for (uint32 ii = 0; ii<size; ii++)
    {
        pStr = mono_array_get(monoArray, MonoObject*, ii);
        T* tmp = convert<T>(pStr);
        arr.push_back(tmp);
    }

    return arr;
}


template<class T>
static T* convert(MonoObject* self)
{
    T* native_handle = getCppFromMono<T>(self);

    //MonoClass* monoClass;

    //monoClass = mono_object_get_class(self);

    //monoClas = mono_class_from_name(image, "TestAllRuntime", "ClassA");

    //MonoClassField* field;
    //field = mono_class_get_field_from_name(monoClass, "native_handle");

    //mono_field_get_value(self, field, (void*)&native_handle);

    return native_handle;
}

template<class T>
static void Class_bind(MonoObject* self)
{
    T* native_handle = new T();

    //MonoClass* monoClass;

    //monoClass=mono_object_get_class(self);

    //monoClas = mono_class_from_name(image, "TestAllRuntime", "ClassA");

    //MonoClassField* field;
    //field = mono_class_get_field_from_name(monoClass, "native_handle");

    //mono_field_set_value(self, field, (void*)&native_handle);
    //T* handle=getCppFromMono<T>(self);

    bindCppAndMono<T>(self,native_handle);

    //handle = native_handle;

    //native_handle->setMonoObject(self);
    //bindCppAndMono(self,t);
}

static void ClassA_bind(MonoObject* self)
{
    Class_bind<ClassA>(self);
}

static void ClassA_setInt(MonoObject* self, int intValue)
{
    //ClassA* a = getCppFromMono<ClassA>(self);

    ClassA* a = convert<ClassA>(self);

    a->setInt(intValue);

    //a->setInt(intValue);
}

static int ClassA_getInt(MonoObject* self)
{
    //ClassA* a = getCppFromMono<ClassA>(self);

    ClassA* a = convert<ClassA>(self);

    return a->getInt();
    //return a->getInt();
}

MonoImage* image;

static void ClassA_setFloat(MonoObject* self, float floatValue)
{
    //ClassA* a = getCppFromMono<ClassA>(self);

    ClassA* a = convert<ClassA>(self);

    a->setFloat(floatValue);

    //self->setFloat(floatValue);
}

static float ClassA_getFloat(MonoObject* self)
{
    //ClassA* a = getCppFromMono<ClassA>(self);

    ClassA* a = convert<ClassA>(self);

    return a->getFloat();

    //return self->getFloat();
}

static void ClassA_setString(MonoObject* self, MonoString* stringValue)
{
    //ClassA* a = getCppFromMono<ClassA>(self);

    ClassA* a =convert<ClassA>(self);

    char* pStr = mono_string_to_utf8(stringValue);
    std::string str = pStr;
    mono_free(pStr);
    //std::cout << str<<std::endl;

    //str = "test gulugulu";
    //std::string aaa = "tset aaaaab";
    a->setString(str);
}

static MonoString* ClassA_getString(MonoObject* self)
{
    //ClassA* a = getCppFromMono<ClassA>(self);

    ClassA* a = convert<ClassA>(self);

    std::string s = a->getString();

    //s = "test hahahahahaha";
    //cout <<"haha  "<< s << endl;
    //mono_string_new_wrapper
    MonoString* str = mono_string_new(domain, s.c_str());
    //MonoString* str = mono_string_new_wrapper(s.c_str());
    //MonoString* str = mono_string_new(domain, s.c_str()); 
    //mono_string_new_wrapper
    return str;
}

static void ClassA_setDirection(MonoObject* self, Direction dir)
{
    ClassA* t = convert<ClassA>(self);

    t->setDirection(dir);

}

static void ClassA_setData(MonoObject* self, MonoArray* data)
{
    std::vector<ClassB*> vec = MonoArrayToVector<ClassB>(data);

    ClassA* tmp = convert<ClassA>(self);

    tmp->setData(vec);
}

static MonoArray* ClassA_getData(MonoObject* self)
{
    ClassA* tmp = convert<ClassA>(self);

    std::vector<ClassB*> vec = tmp->getData();

    MonoClass* cl = mono_class_from_name(image,"TestAllRuntime","ClassB");

    return VectorToMonoArray<ClassB>(vec,cl);
}

static void ClassA_setCallback(MonoObject* self, MonoObject* object,MonoString* funcName)
{
    MonoClass* c = mono_object_get_class(self);
    MonoClass* b = mono_object_get_class(object);

    char* pStr = mono_string_to_utf8(funcName);
    std::string str = pStr;
    mono_free(pStr);

    MonoMethod* m= mono_class_get_method_from_name(b,str.c_str(),0);

    mono_runtime_invoke(m,self,NULL,NULL);
}

void ClassB_bind(MonoObject* self)
{
    Class_bind<ClassB>(self);
}

int ClassB_getValue(MonoObject* self)
{
    ClassB* tmp = convert<ClassB>(self);

    return tmp->getValue();
}

void ClassB_setValue(MonoObject* self, int value)
{
    ClassB* tmp = convert<ClassB>(self);

    tmp->setValue(value);
}

void ClassC_bind(MonoObject* self)
{
    Class_bind<ClassC>(self);
}

float ClassC_getFloat(MonoObject* self)
{
    ClassC* tmp = convert<ClassC>(self);

    return tmp->getFloat();
}

void ClassC_setFloat(MonoObject* self,float value)
{
    ClassC* tmp = convert<ClassC>(self);

    tmp->setFloat(value);
}

void ClassD_bind(MonoObject* self)
{
    Class_bind<ClassD>(self);
}

float ClassD_getFF(MonoObject* self)
{
    ClassD* tmp = convert<ClassD>(self);

    return tmp->getFF();
}

void ClassD_setFF(MonoObject* self,float ff)
{
    ClassD* tmp = convert<ClassD>(self);

    tmp->setFF(ff);
}

void main()
{
    // 設定搜尋路徑,主要用於搜尋mscorlib.dll
    std::string MonoPath = "C:/Program Files (x86)/Mono";
    std::string ManagedLibraryPath = "E:/vsPrjs/Dinky2D/TestAllRuntime/TestAllRuntime.dll";

    std::string monoPath = MonoPath;
    std::string monoLibPath = monoPath + "/lib";
    std::string monoEtcPath = monoPath + "/etc";

    mono_set_dirs(monoLibPath.c_str(),
        monoEtcPath.c_str());
    // Program.cs所編譯dll所在的位置
    const char* managed_binary_path = ManagedLibraryPath.c_str();

    //獲取應用域
    domain = mono_jit_init("TestAllRuntime");
    mono_config_parse(NULL);

    //載入程式集ManagedLibrary.dll
    MonoAssembly* assembly = mono_domain_assembly_open(domain, managed_binary_path);
    image = mono_assembly_get_image(assembly);

    //====================================================================
    // -----------------------

    // -- Base - Texture
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_bind",
    reinterpret_cast<void*>(&ClassA_bind));
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_setInt", reinterpret_cast<void*>(&ClassA_setInt));
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_getInt", reinterpret_cast<void*>(&ClassA_getInt));
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_setFloat", reinterpret_cast<void*>(&ClassA_setFloat));
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_getFloat", reinterpret_cast<void*>(&ClassA_getFloat));
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_setString", reinterpret_cast<void*>(&ClassA_setString));
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_getString", reinterpret_cast<void*>(&ClassA_getString));
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_setDirection", reinterpret_cast<void*>(&ClassA_setDirection));
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_setData",
    reinterpret_cast<void*>(&ClassA_setData)
    );
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_getData",
    reinterpret_cast<void*>(&ClassA_getData)
    );
    mono_add_internal_call("TestAllRuntime.ClassA::ClassA_setCallback",
        reinterpret_cast<void*>(&ClassA_setCallback)
    );

    mono_add_internal_call("TestAllRuntime.ClassB::ClassB_bind", reinterpret_cast<void*>(&ClassB_bind));
    mono_add_internal_call("TestAllRuntime.ClassB::ClassB_getValue", reinterpret_cast<void*>(&ClassB_getValue));
    mono_add_internal_call("TestAllRuntime.ClassB::ClassB_setValue", reinterpret_cast<void*>(&ClassB_setValue));
    mono_add_internal_call("TestAllRuntime.ClassC::ClassC_bind", reinterpret_cast<void*>(&ClassC_bind));
    mono_add_internal_call("TestAllRuntime.ClassC::ClassC_getFloat", reinterpret_cast<void*>(&ClassC_getFloat));
    mono_add_internal_call("TestAllRuntime.ClassC::ClassC_setFloat", reinterpret_cast<void*>(&ClassC_setFloat));
    mono_add_internal_call("TestAllRuntime.ClassD::ClassD_bind", reinterpret_cast<void*>(&ClassD_bind));
    mono_add_internal_call("TestAllRuntime.ClassD::ClassD_getFF", reinterpret_cast<void*>(&ClassD_getFF));
    mono_add_internal_call("TestAllRuntime.ClassD::ClassD_setFF", reinterpret_cast<void*>(&ClassD_setFF));
    //===============================================================================
    MonoClass* main_class = mono_class_from_name(image, "TestAllRuntime", "Main");

    const bool include_namespace = true;
    MonoMethodDesc* managed_method_desc = mono_method_desc_new("TestAllRuntime.Main:main()", include_namespace);
    MonoMethod* managed_method = mono_method_desc_search_in_class(managed_method_desc, main_class);
    mono_method_desc_free(managed_method_desc);

    mono_runtime_invoke(managed_method, NULL, NULL, NULL);

    mono_jit_cleanup(domain);
}

ClassA.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Runtime.CompilerServices;
using System.Collections;

namespace TestAllRuntime
{
    public enum Direction
    {
        right,front,left,back
    }

    public delegate void Callback();

    public class ClassA : Base
    {
        public void print()
        {
            Console.WriteLine("print line");
        }

        public void setCallback(Callback func)
        {
            ClassB b = (ClassB)func.Target;
            Console.WriteLine(b.Value);
            ClassA_setCallback(this, b ,func.Method.Name);
        }

        public ClassA()
        {
            ClassA_bind(this);
        }

        public int intValue
        {
            get
            {
                return ClassA_getInt(this);
            }

            set
            {
                ClassA_setInt(this, value);
            }
        }

        public float floatValue
        {
            get
            {
                return ClassA_getFloat(this);
            }

            set
            {
                ClassA_setFloat(this, value);
            }
        }

        public string getString()
        {
            return ClassA_getString(this);
        }

        public void setString(String s)
        {
            ClassA_setString(this, s);
        }

        public string stringValue
        {
            get
            {
                return ClassA_getString(this);
            }

            set
            {
                ClassA_setString(this, value);
            }
        }

        public Direction dir
        {
            get
            {
                return ClassA_getDirection(this);
            }

            set
            {
                ClassA_setDirection(this, value);
            }
        }

        public ClassB[] data
        {
            get
            {
                return ClassA_getData(this);
            }

            set
            {
                ClassA_setData(this, value);
            }
        }

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern static void ClassA_bind(ClassA self);
        //private IntPtr native_handle=(IntPtr)0;

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern static void ClassA_setInt(ClassA self, int intValue);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern static int ClassA_getInt(ClassA self);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern static void ClassA_setFloat(ClassA self, float floatValue);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern static float ClassA_getFloat(ClassA self);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern static void ClassA_setString(ClassA self, String stringValue);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern static string ClassA_getString(ClassA self);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern static void ClassA_setDirection(ClassA self, Direction dir);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern static Direction ClassA_getDirection(ClassA self);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern ClassB[] ClassA_getData(ClassA self);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void ClassA_setData(ClassA self, ClassB[] data);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void ClassA_setCallback(ClassA self, ClassB obj, string funcName);

    }

    public class ClassB : Base
    {
        public void printb()
        {
            Console.WriteLine("print b");
        }

        public ClassB()
        {
            ClassB_bind(this);
        }

        public int Value
        {
            get
            {
                return ClassB_getValue(this);
            }

            set
            {
                ClassB_setValue(this, value);
            }
        }

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void ClassB_bind(ClassB self);
        //private IntPtr native_handle = (IntPtr)0;

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void ClassB_setValue(ClassB self, int value);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern int ClassB_getValue(ClassB self);

    }

    public class ClassC : ClassB
    {
        public ClassC()
        {
            ClassC_bind(this);
        }

        public float Float
        {
            get
            {
                return ClassC_getFloat(this);
            }

            set
            {
                ClassC_setFloat(this, value);
            }
        }


        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void ClassC_bind(ClassC self);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void ClassC_setFloat(ClassC self, float value);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern float ClassC_getFloat(ClassC self);

    }

    public class ClassD : ClassB
    {
        public ClassD()
        {
            ClassD_bind(this);
        }

        public float FF
        {
            get
            {
                return ClassD_getFF(this);
            }

            set
            {
                ClassD_setFF(this, value);
            }
        }


        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void ClassD_bind(ClassD self);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void ClassD_setFF(ClassD self, float ff);

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern float ClassD_getFF(ClassD self);

    }

}

Main.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;


namespace TestAllRuntime
{
    class Main
    {

        public static void main()
        {
            // ClassA a = new ClassA();

            // a.intValue = 5;
            // a.floatValue = 10.3f;
            //// Console.WriteLine(getMemory(a));

            // string s = "hello word 哈哈哈";
            // a.setString(s);

            // a.dir = Direction.left;
            // Console.WriteLine(a.intValue);

            //Console.WriteLine(a.floatValue);

            // Console.WriteLine(a.getString());

            // Console.Read();

            Clas