ILRuntime介绍
ILRuntime项目为基于C#的平台(例如Unity)提供了一个纯C#实现,快速、方便且可靠的IL运行时,使得能够在不支持JIT的硬件环境(如iOS)能够实现代码的热更新。
ILRuntime 官方中文手册
ILRuntime的优势
同市面上的其他热更方案相比,ILRuntime主要有以下优点:
无缝访问C#工程的现成代码,无需额外抽象脚本API
直接使用VS2015进行开发,ILRuntime的解译引擎支持.Net 4.6编译的DLL
执行效率是L#的10-20倍
选择性的CLR绑定使跨域调用更快速,绑定后跨域调用的性能能达到slua的2倍左右(从脚本调用GameObject之类的接口)
支持跨域继承
完整的泛型支持
拥有Visual Studio的调试插件,可以实现真机源码级调试。支持Visual Studio 2015 Update3 以及Visual Studio 2017和Visual Studio 2019
导入ILRuntime插件
在Unity2018以上版本中开始使用ILRuntime
ILRuntime1.6版新增了Package Manager发布,使用Unity2018以上版本可以直接通过Package Manager安装,具体方法如下
通过Unity的Window->Package Manager菜单,打开Package Manager,将上部标签页选项选择为All Packages,Advanced里勾上Show Preview Packages,等待Unity加载完包信息,应该就能在左侧列表中找到ILRuntime,点击安装即可
配置使用非安全代码
导入插件后还需要做一些设置 打开Fill->Build Settings->Player Settings->Other Settings
将允许使用不安全代码勾选上
ILRunTime的使用
热更环境配置
1、在Unity Assers文件夹下新建三个文件夹分别为Hotfix、Model、ThirdParty三个文件夹分别为热更文件夹、Unity主工程文件夹、第三方库文件夹。在每个文件夹下新建一个对应的程序集
新建程序集时需要注意三个程序集都要勾选允许不安全代码这个选项
程序集的名字自定义
热更Hotfix程序集 只是在编译器模式下使用
Model、ThirdParty程序集在任何平台都可以使用
最后点击右下角的Apply保存应用
接着就是程序集之间添加定义引用
热更程Unity.Hotfix序集 会用到主工程 第三方库 和IL库
Unity.Model
ThirdParty
ILRuntime位于 Packges->ILRuntime中
Unity中程序集定义的作用
在Hotfix,Model ThirdParty文件里新建脚本(包括子文件里面的脚本)后,该脚本就会自动编译到对应的程序集中。所以之后新加的脚本就放在Hotfix、Mode、ThirtyParty这三个文件中。
ILRuntime dll文件复制
在Assets中新建一个Editor文件夹和一个存放dll的文件夹
在Edito文件夹中新建一个子文件夹。然后新建一个脚本命名为BuildHotfixEditor
using UnityEngine;
using UnityEditor;
using System.IO;
//Unity 自带特性 每次编译完脚本后都会自动编译(编译器模式下)
[InitializeOnLoad]
public class BuildHotfixEditor
{
//程序集路径
const string scriptAssemblies = "Library/ScriptAssemblies";
//目标路径
const string codeDir = "Assets/Res/Code";
//hotfixdll
const string hotfixDll = "Unity.Hotfix.dll";
//Hotfixpdb
const string hotfixPdb = "Unity.Hotfix.pdb";
static BuildHotfixEditor()
{
//目标文件 (必须要添加.bytes后缀)
string fixDll = Path.Combine(codeDir,$"{hotfixDll}.bytes");
string fixPdb = Path.Combine(codeDir,$"{hotfixPdb}.bytes");
//开始复制
File.Copy(Path.Combine(scriptAssemblies,hotfixDll),fixDll,true);
File.Copy(Path.Combine(scriptAssemblies,hotfixPdb),fixPdb,true);
//刷新
AssetDatabase.Refresh();
Debug.Log("程序集拷贝完成");
}
}
编译完成后 回到Unity后 会看到Code文件夹了多了两个文件。以后每次编译完脚本后 那两个文件都会自动更新
接着创建一个预制件命名为Code给它添加一个脚本CodeRefrence,该脚本放到Mode模块下
using UnityEngine;
public class CodeRefrence : MonoBehaviour
{
public TextAsset HotfixDll;
public TextAsset HotfixPdb;
}
最后将Code保存为预制件 。以后加载热更文件就从这里去加载了
通过代码加载热更dll
脚本初始化加载
首先给工程加上ILRuntime宏定义
获取缓存的bytes文件、构建AppDomain对象、通过LoadAssebly进行加载
在Mode模块中添加一个脚本HotFixManager
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using ILRuntime.Mono.Cecil.Pdb;
using ILRuntime.CLR.TypeSystem;
using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Intepreter;
using UnityEngine.Events;
using System;
using Unity.Model;
using ILRuntime.Runtime.Stack;
using System.Reflection;
public class HotFixManager : MonoBehaviour
{
public GameObject code;
//内存流
MemoryStream hotfixdllstream;
MemoryStream hotfixpdbstream;
//构建一个AppDoMain
ILRuntime.Runtime.Enviorment.AppDomain appDomain;
private void Start()
{
Load();
}
void Load()
{
//获取缓存的dll pdb两个bytes文件
CodeRefrence cr = code.GetComponent<CodeRefrence>();
byte[] hotfixdll = cr.HotfixDll.bytes;
byte[] hotfixpdb = cr.HotfixPdb.bytes;
#if ILRuntime
//ILRuntime模式下 将bytes文件缓存到内存流中
hotfixdllstream = new MemoryStream(hotfixdll);
hotfixpdbstream = new MemoryStream(hotfixpdb);
//构建AppDoMain对象 通过它的LoadAssembly来经行加载
appDomain = new ILRuntime.Runtime.Enviorment.AppDomain();
appDomain.LoadAssembly(hotfixdllstream, hotfixpdbstream, new PdbReaderProvider());
Debug.Log("ILRunTime模式下加载程序集完成");
#else
//普通平台下
Assembly.Load(hotfixdll, hotfixpdb);
#endif
}
}
热更模块Init全部代码 下面出现的那些方法 都在这里
using Unity.Model;
using UnityEngine;
using UnityEngine.UI;
namespace Unity.Hotfix
{
public class Init :UIBase
{
public static void Log()
{
Debug.Log("Log1被调用");
}
public static void Log(string str)
{
Debug.Log("Log2被调用:"+str);
}
public static void Log(string str,string str1)
{
Debug.Log("Log3被调用:" + str+":"+str1);
}
public Init()
{
Debug.Log("调用无参构造函数");
}
public Init(string str)
{
Debug.Log("调用有参构造函数"+str);
}
//要封装属性 外部才能修改 获取
private string name="冰封战神";
public string Name {
get { return name; }
set { name = value; }
}
public int age;
public void AddNum(int a,int b)
{
int c = a + b;
Debug.Log("计算结果为:"+ c);
}
public void GenericFunction<T>(T t)
{
Debug.Log("调用了泛型方法:"+t);
}
public void GenericFunction<T,G>(T t,G g)
{
Debug.Log("调用了泛型方法:参数一" + t);
Debug.Log("调用了泛型方法:参数二" + g);
}
public void BtnOnclic()
{
GameObject.Find("Canvas/btn").GetComponent<Button>().onClick.AddListener(() => { Debug.Log("按钮被点击了"); });
}
public override void HandlerEvent(int id)
{
Debug.Log("抽象方法被调用"+id);
}
public override string UIname
{
get
{
return name;
}
}
public override void Open(string text)
{
base.Open(text);
Debug.Log("子类方法被调用 "+text);
}
public override int AddNum_1(int a, int b)
{
Debug.Log("Init子类方法"+a);
return base.AddNum_1(a, b);
}
}
}
HotFix全部代码
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using ILRuntime.Mono.Cecil.Pdb;
using ILRuntime.CLR.TypeSystem;
using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Intepreter;
using UnityEngine.Events;
using System;
using Unity.Model;
using ILRuntime.Runtime.Stack;
using System.Reflection;
public class HotFixManager : MonoBehaviour
{
public GameObject code;
//内存流
MemoryStream hotfixdllstream;
MemoryStream hotfixpdbstream;
//构建一个AppDoMain
ILRuntime.Runtime.Enviorment.AppDomain appDomain;
private void Start()
{
Load();
}
void Load()
{
//获取缓存的dll pdb两个bytes文件
CodeRefrence cr = code.GetComponent<CodeRefrence>();
byte[] hotfixdll = cr.HotfixDll.bytes;
byte[] hotfixpdb = cr.HotfixPdb.bytes;
#if ILRuntime
//ILRuntime模式下 将bytes文件缓存到内存流中
hotfixdllstream = new MemoryStream(hotfixdll);
hotfixpdbstream = new MemoryStream(hotfixpdb);
//构建AppDoMain对象 通过它的LoadAssembly来经行加载
appDomain = new ILRuntime.Runtime.Enviorment.AppDomain();
appDomain.LoadAssembly(hotfixdllstream, hotfixpdbstream, new PdbReaderProvider());
Debug.Log("ILRunTime模式下加载程序集完成");
RegisterDelegata();//ILRuntime 热更中支支持action 没有返回值的委托 Func 带有返回值的这两种委托
appDomain.RegisterCrossBindingAdaptor(new UIBaseAdaptor()); //注册跨域继承适配器
RegisterCLRMethod();//注册CLR重定向
#else
//普通平台下
Assembly.Load(hotfixdll, hotfixpdb);
#endif
}
/// <summary>
/// 注册跨域继承适配器
/// </summary>
void RegisterAdaptor()
{
appDomain.RegisterCrossBindingAdaptor(new UIBaseAdaptor());
}
/// <summary>
/// 注册CLR
/// </summary>
unsafe void RegisterCLRMethod()
{
MethodInfo logMethod = typeof(Debug).GetMethod("Log", new Type[] {typeof(object)});
appDomain.RegisterCLRMethodRedirection(logMethod, DLog);
}
/// <summary>
/// CLR带参数的方法的重定向重定向
/// </summary>
/// <param name="__intp"></param>
/// <param name="__esp"></param>
/// <param name="__mStack"></param>
/// <param name="__method"></param>
/// <param name="isNewObj"></param>
/// <returns></returns>
public unsafe static StackObject* DLog(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
{
ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
StackObject* ptr_of_this_method;
//只有一个参数,所以返回指针就是当前栈指针ESP - 1
StackObject* __ret = ILIntepreter.Minus(__esp, 1);
//第一个参数为ESP -1, 第二个参数为ESP - 2,以此类推
ptr_of_this_method = ILIntepreter.Minus(__esp, 1);
//获取参数message的值
object message = StackObject.ToObject(ptr_of_this_method, __domain, __mStack);
//需要清理堆栈
__intp.Free(ptr_of_this_method);
//如果参数类型是基础类型,例如int,可以直接通过int param = ptr_of_this_method->Value获取值,
//关于具体原理和其他基础类型如何获取,请参考ILRuntime实现原理的文档。
//通过ILRuntime的Debug接口获取调用热更DLL的堆栈
string stackTrace = __domain.DebugService.GetStackTrace(__intp);
Debug.Log(string.Format("{0}\n{1}", message, stackTrace));
return __ret;
}
public void CallHotfixMethod()
{
appDomain.Invoke("Unity.Hotfix.Init","Log",null,null);
appDomain.Invoke("Unity.Hotfix.Init", "Log", null,"Hello ILRuntime");
appDomain.Invoke("Unity.Hotfix.Init", "Log", null,new string[] { "Hello ILRuntime","欢迎来到IL热更中"});
//
IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];
//根据参数个数 去调用方法
List<IType> param = new List<IType>();
param.Add(appDomain.GetType(typeof(string)));//参数的类型 和个数
param.Add(appDomain.GetType(typeof(string)));//参数的类型 和个数
IMethod method= type.GetMethod("Log", param,null);
//要调用的方法 实例 需要传递的参数
appDomain.Invoke(method,null,"哈哈哈");
appDomain.Invoke(method,null,new string[] {"多参数调用","两个参数" });
}
public void CallInstantiateFunction()
{
appDomain.Instantiate("Unity.Hotfix.Init",null);
appDomain.Instantiate("Unity.Hotfix.Init",new string[] {"你好 ILRuntime" });
//实例化后 并且调用不带参数的构造函数
IType type= appDomain.LoadedTypes["Unity.Hotfix.Init"];
ILTypeInstance itype= ((ILType)type).Instantiate();
//调用带参数的构造函数 直接将参数传递进去 就行了
((ILType)type).Instantiate(new string[] {"我又来了 ILRuntime" });
}
public void CallMemberFunction()
{
//获取实例
IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];
ILTypeInstance itype = ((ILType)type).Instantiate();
//调用成员方法
appDomain.Invoke("Unity.Hotfix.Init","AddNum",itype,new object[] { 3,5});
//修改成员变量
appDomain.Invoke("Unity.Hotfix.Init","set_Name",itype,new object[] { "冰封战神"});
string name = (string)appDomain.Invoke("Unity.Hotfix.Init", "get_Name", itype, null);
Debug.Log(name);
}
public void CallGenericMethod()
{
//获取实例
IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];
ILTypeInstance itype = ((ILType)type).Instantiate();
//根据参数个数 去调用方法
IType[] genericArguments = new IType[2]; //泛型的参数类型 为string
genericArguments[0] = appDomain.GetType(typeof(string));
genericArguments[1] = appDomain.GetType(typeof(int));
appDomain.InvokeGenericMethod("Unity.Hotfix.Init", "GenericFunction",genericArguments,itype,new object[] { "Hello ILRuntime 泛型方法调用",990});
}
/// <summary>
/// 委托转换
/// ILRuntime 热更中支支持action 没有返回值的委托 Func 带有返回值的这两种委托
/// </summary>
void RegisterDelegata()
{
//将Unity中的 UnityAction委托 转换为Action委托
appDomain.DelegateManager.RegisterDelegateConvertor<UnityAction>((Uityact) =>
{
return new UnityAction(() =>{ ((Action)Uityact)(); });
});
//Action 带有一个参数 没有返回值的委托注册
appDomain.DelegateManager.RegisterMethodDelegate<string>();
//Func 带有返回值 和参数的委托注册
appDomain.DelegateManager.RegisterFunctionDelegate<int,string,string>();
// UnityAction 带有 参数的委托注册
appDomain.DelegateManager.RegisterDelegateConvertor<UnityAction<String>>((act)=>
{ return new UnityAction<string>((arg) => { ((Action<string>)act)(arg); }); });
}
void BtnText()
{
//获取实例
IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];
ILTypeInstance itype = ((ILType)type).Instantiate();
appDomain.Invoke("Unity.Hotfix.Init", "BtnOnclic",itype,null);
}
//跨域继承
void InherFunction()
{
//创建一个UIBase实例
UIBase UIbase;
UIbase = appDomain.Instantiate<UIBase>("Unity.Hotfix.Init");
UIbase.Open("打开");
Debug.Log(UIbase.UIname);
UIbase.HandlerEvent(888);
UIbase.AddNum_1(10,8);
}
}
调用热更中的静态方法
热更新中类的限制:一定要写命名空间,不要直接继承Mono
在热更模块中 新建与i个Init脚本,打开脚本 把命名空间设置为Unity.Hotfix
namespace Unity.Hotfix
{
public class Init
{
public static void Log()
{
Debug.Log("Log1被调用");
}
public static void Log(string str)
{
Debug.Log("Log2被调用:"+str);
}
public static void Log(string str,string str1)
{
Debug.Log("Log3被调用:" + str+":"+str1);
}
}
}
在刚才添加的HotFixManager脚本中新加一个调用热更中静态方法的方法
public void CallHotfixMethod()
{
//调用无参数静态方法,appdomain.Invoke("类名", "方法名", 对象引用, 参数列表);
appDomain.Invoke("Unity.Hotfix.Init","Log",null,null);
appDomain.Invoke("Unity.Hotfix.Init", "Log", null,"Hello ILRuntime");
appDomain.Invoke("Unity.Hotfix.Init", "Log", null,new string[] { "Hello ILRuntime","欢迎来到IL热更中"});
//通过IMethod调用方法
//预先获得IMethod,可以减低每次调用查找方法耗用的时间
IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];
//参数类型列表
List<IType> param = new List<IType>();
param.Add(appDomain.GetType(typeof(string)));//参数的类型 和个数
param.Add(appDomain.GetType(typeof(string)));//参数的类型 和个数
//根据方法名称和参数个数获取方法
IMethod method= type.GetMethod("Log", param,null);
//要调用的方法 实例 需要传递的参数
appDomain.Invoke(method,null,"哈哈哈");
appDomain.Invoke(method,null,new string[] {"多参数调用","两个参数" });
}
对热更中的类经行实例化
public void CallInstantiateFunction()
{
appDomain.Instantiate("Unity.Hotfix.Init",null);//调用无参构造方法
appDomain.Instantiate("Unity.Hotfix.Init",new string[] {"你好 ILRuntime" });//调用有参构造方法
//实例化后 并且调用不带参数的构造函数
IType type= appDomain.LoadedTypes["Unity.Hotfix.Init"];
ILTypeInstance itype= ((ILType)type).Instantiate();
//调用带参数的构造函数 直接将参数传递进去 就行了
((ILType)type).Instantiate(new string[] {"我又来了 ILRuntime" });
}
调用实例中的变量和方法
public void CallMemberFunction()
{
//获取实例
IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];
ILTypeInstance itype = ((ILType)type).Instantiate();
//调用成员方法
appDomain.Invoke("Unity.Hotfix.Init","AddNum",itype,new object[] { 3,5});
//修改成员变量
appDomain.Invoke("Unity.Hotfix.Init","set_Name",itype,new object[] { "冰封战神"});
//获取成员变量
string name = (string)appDomain.Invoke("Unity.Hotfix.Init", "get_Name", itype, null);
Debug.Log(name);
}
调用热更中的泛型方法
public void CallGenericMethod()
{
//获取实例
IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];
ILTypeInstance itype = ((ILType)type).Instantiate();
//根据参数个数 去调用方法
IType[] genericArguments = new IType[2]; //泛型的参数类型 为string
genericArguments[0] = appDomain.GetType(typeof(string));
genericArguments[1] = appDomain.GetType(typeof(int));
appDomain.InvokeGenericMethod("Unity.Hotfix.Init", "GenericFunction",genericArguments,itype,new object[] { "Hello ILRuntime 泛型方法调用",990});
}
按钮点击事件监听与委托转换
void BtnText()
{
//获取实例
IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];
ILTypeInstance itype = ((ILType)type).Instantiate();
appDomain.Invoke("Unity.Hotfix.Init", "BtnOnclic",itype,null);
}
/// <summary>
/// 委托转换
/// ILRuntime 热更中支支持action 没有返回值的委托 Func 带有返回值的这两种委托
/// </summary>
void RegisterDelegata()
{
//将Unity中的 UnityAction委托 转换为Action委托
appDomain.DelegateManager.RegisterDelegateConvertor<UnityAction>((Uityact) =>
{
return new UnityAction(() =>{ ((Action)Uityact)(); });
});
//Action 带有一个参数 没有返回值的委托注册
appDomain.DelegateManager.RegisterMethodDelegate<string>();
//Func 带有返回值 和参数的委托注册 最后一个时返回值类型 前面的是参数类型
appDomain.DelegateManager.RegisterFunctionDelegate<int,string,string>();
// UnityAction 带有 参数的委托注册
appDomain.DelegateManager.RegisterDelegateConvertor<UnityAction<String>>((act)=>
{ return new UnityAction<string>((arg) => { ((Action<string>)act)(arg); }); });
}
要在dll加载完成后 经行注册调用
跨域继承适配器
using UnityEngine;
namespace Unity.Model
{
public abstract class UIBase
{
private string Uiname="萧索之刃";
public virtual string UIname
{
get { return UIname; }
set { Uiname = value; }
}
public virtual void Open(string text)
{
Debug.Log("父类的方法被调用"+text);
}
public abstract void HandlerEvent(int id);
public virtual int AddNum_1(int a,int b)
{
int c = a + b;
Debug.Log("UIBase"+c);
return c;
}
}
}
namespace Unity.Hotfix
{
public class Init :UIBase
{
public override void HandlerEvent(int id)
{
Debug.Log("抽象方法被调用"+id);
}
public override string UIname
{
get
{
return name;
}
}
public override void Open(string text)
{
base.Open(text);
Debug.Log("子类方法被调用 "+text);
}
public override int AddNum_1(int a, int b)
{
Debug.Log("Init子类方法"+a);
return base.AddNum_1(a, b);
}
}
}
using ILRuntime.Runtime.Enviorment;
using ILRuntime.Runtime.Intepreter;
using System;
using UnityEngine;
//适配器
public class UIBaseAdaptor : CrossBindingAdaptor
{
//定义访问方法的方法信息
//一个参数的方法
static CrossBindingMethodInfo<string> mOpen = new CrossBindingMethodInfo<string>("Open");
static CrossBindingMethodInfo<int> mhanderEvent = new CrossBindingMethodInfo<int>("HandlerEvent");
static CrossBindingMethodInfo<string> set_mUiname = new CrossBindingMethodInfo<string>("set_UIname");
//有一个返回值的方法
static CrossBindingFunctionInfo<string> get_mUiname = new CrossBindingFunctionInfo<string>("get_UIname");
//func委托 最后一个是返回值类型 前面的是参数类型
static CrossBindingFunctionInfo<int,int,int> mAdd = new CrossBindingFunctionInfo<int,int,int>("AddNum_1");
public override Type BaseCLRType
{
get
{
return typeof(Unity.Model.UIBase);//这里是想要继承的类型
}
}
public override Type AdaptorType
{
get
{
return typeof(Adapter);
}
}
public override object CreateCLRInstance(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance)
{
return new Adapter(appdomain, instance);
}
public class Adapter : Unity.Model.UIBase, CrossBindingAdaptorType
{
ILTypeInstance instance;
ILRuntime.Runtime.Enviorment.AppDomain appDomain;
//必须要提供一个无参的构造方法
public Adapter() { }
public Adapter(ILRuntime.Runtime.Enviorment.AppDomain appDomain, ILTypeInstance instance)
{
this.appDomain = appDomain;
this.instance = instance;
}
public ILTypeInstance ILInstance
{
get
{
return instance;
}
}
//下面将 所有的函数重载一遍 并中转到热更内
//虚方法必须判断当前是否已经再调用中 否则如果脚本类中调用base.Open(text) 就会无限循环 最终导致爆栈
public override void Open(string text)
{
if (mOpen.CheckShouldInvokeBase(this.instance))
base.Open(text);
else
mOpen.Invoke(this.instance,text);
}
public override void HandlerEvent(int id)
{
mhanderEvent.Invoke(this.instance,id);
Debug.Log("适配器中调用");
}
public override string UIname
{
get
{
if (get_mUiname.CheckShouldInvokeBase(this.instance))
return base.UIname;
else
return get_mUiname.Invoke(this.instance);
}
set
{
if (set_mUiname.CheckShouldInvokeBase(this.instance))
base.UIname = value;
else
set_mUiname.Invoke(this.instance,value);
}
}
public override int AddNum_1(int a, int b)
{
if (mAdd.CheckShouldInvokeBase(this.instance))
return base.AddNum_1(a, b);
else
return mAdd.Invoke(this.instance,a,b);
}
}
}
CLR重定向
public unsafe static StackObject* DLog(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
{
ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
StackObject* ptr_of_this_method;
//只有一个参数,所以返回指针就是当前栈指针ESP - 1
StackObject* __ret = ILIntepreter.Minus(__esp, 1);
//第一个参数为ESP -1, 第二个参数为ESP - 2,以此类推
ptr_of_this_method = ILIntepreter.Minus(__esp, 1);
//获取参数message的值
object message = StackObject.ToObject(ptr_of_this_method, __domain, __mStack);
//需要清理堆栈
__intp.Free(ptr_of_this_method);
//如果参数类型是基础类型,例如int,可以直接通过int param = ptr_of_this_method->Value获取值,
//关于具体原理和其他基础类型如何获取,请参考ILRuntime实现原理的文档。
//通过ILRuntime的Debug接口获取调用热更DLL的堆栈
string stackTrace = __domain.DebugService.GetStackTrace(__intp);
Debug.Log(string.Format("{0}\n{1}", message, stackTrace));
return __ret;
}
注册CLR重定向 同样需要在Load方法中调用
unsafe void RegisterCLRMethod()
{
MethodInfo logMethod = typeof(Debug).GetMethod("Log", new Type[] {typeof(object)});
appDomain.RegisterCLRMethodRedirection(logMethod, DLog);
}