zhouqijie

一、CPP和Csharp的交互概述

要提供脚本功能,将Mono运行时嵌入C/C++程序之后,只是在C/C++程序中调用C#中定义的方法显然还是不够的。脚本机制的最终目的还是希望能够在脚本语言中使用原生的代码。

为了使托管代码能够和非托管代码交互,我们需要在C#文件中引入命名空间System.Runtime.CompilerServices,同时需要提供一个IntPtr类型的句柄以便于托管代码和非托管代码之间引用数据。
(IntPtr 类型被设计成整数,其大小适用于特定平台。 即是说,此类型的实例在 32 位硬件和操作系统中将是 32 位,在 64 位硬件和操作系统上将是 64 位。IntPtr 对象常可用于保持句柄。 例如,IntPtr 的实例广泛地用在 System.IO.FileStream 类中来保持文件句柄。)

C#端主要目的是为游戏脚本提供相应的接口,而非具体逻辑的实现。将C#类对象的构建工作以及各种具体实现逻辑移交给非托管代码C++。

CRE:对于引擎底层组件:
1.C#端不存储任何数据,除了IntPtr句柄。
2.C#端不负责任何实现,仅定义接口。

二、C#调用C++(内部调用)

返回值/参数类型传递:

类型传递见补充。

命名空间:

using System.Runtime.CompilerServices;

C#端方法签名:

[MethodImpl(MethodImplOptions.InternalCall)]
public extern static CSType CSMethod(...);

  1. 通常为静态方法,通常有一个IntPtr句柄参数传递对象地址。

C++端函数定义:

CPPType CPPMethod(){//……}

关联:

mono_add_internal_call("C#命名空间名.C#类名::C#方法名()", reinterpret_cast<void*>(CPPMethod));

  1. 方法名括号可省略。
  2. 对于不调用其他方法的属性实现,默认方法名称get_属性名。传递参数为C#对象(即CPP端的MonoObject*)。

补充:P/Invoke:

CRE:使用P/Invoke也可以实现C#调用C++。Mono扩充过的P/Invoke支持查找不在外部库而在与你程序相同地址空间的内部符号。
CRE:详见文档(http://docs.go-mono.com/index.aspx?link=xhtml%3adeploy%2fmono-api-embedding.html)。





三、C++调用C#

第一步:编写.cs代码并用mcs编译:

//Program.cs
namespace MonoCSharp
{
    public class MainTest
    {
        public static void HelloWorld()
        {
            Console.WriteLine("Hello CSharp!");
            Console.ReadLine();
        }
    }
}

第二步:在C++代码中调用:

//...
//获取MonoClass,类似于反射
MonoClass* main_class = mono_class_from_name(image, "MonoCSharp", "MainTest");

//获取要调用的MonoMethodDesc,主要调用过程
MonoMethodDesc* entry_point_method_desc = mono_method_desc_new("MonoCSharp.MainTest:HelloWorld()", true);
MonoMethod* entry_point_method = mono_method_desc_search_in_class(entry_point_method_desc,main_class);
mono_method_desc_free(entry_point_method_desc);
//调用方法
mono_runtime_invoke(entry_point_method, NULL, NULL, NULL);
//...

注意:mono_runtime_invoke()

第一个参数是方法。
第二个参数是对象,如果是静态方法,则为NULL。
第三个参数是参数(void*数组)。 第四个参数会传回调用时发生的exception。

C++调用Mono的Main函数入口:

CRE:可以调用控制台应用程序的Main函数,也可以调用Gtk#和WinForms的Main函数。

	//Main调用  
	char* args = const_cast<char*>("arg0");
	mono_jit_exec(domain, assembly_editor, 1, &args);