ILRuntime的作用
用于unity游戏的热更新,其语言是由C#写的,所以很受unity工程师的喜爱,这样就不再用xlua脚本进行热更新。 ILRuntime官方讲解
为什么要用到ILRuntime
我们知道他的目的后,它主要是用来进行游戏的更新操作,但是更新的流程是玩家运行游戏查看使用的游戏版本和我们上传到服务器版本是否相同,不同则进行更新操作。所以这里我们就用到了程序之间的跨域使用。而ILRuntime就是充当中介的作用实现跨域操作。
ILRuntime的实现原理
ILRuntime借助Mono.Cecil库来读取DLL的PE信息,以及当中类型的所有信息,最终得到方法的IL汇编码,然后通过内置的IL解译执行虚拟机来执行DLL中的代码。
ILRuntime使用
环境部署
将项目设置更改如下:
.在Assetts中创建HotFix文件夹,Model文件夹两个文件夹,
导入资源(我上传的免费资源)下载下来导入 我们将link文件进行修改
<linker>
<assembly fullname="Unity.Model" preserve="all"/>
<assembly fullname="Unity.ThirdParty" preserve="all"/>
<assembly fullname="unityEngine" preserve="all"/>
<assembly fullname="System" preserve="all"/>
</linker>
将文件中的内容替换成该内容
在Modle文件夹创建Uunity.Model程序集
在HotFix文件夹创建Unity.HotFix程序集 在ThirdParty文件夹创建Unity.ThirdParty程序集…(名字自定义) 接下来对这三个程序集进行修改
Unity.Model程序集 Unity.HotFix程序集 Unity.ThirdParty程序集
很好,现在我们的环境搭建完毕。
生成Unity.Model.dll文件和Unity.HotFix.dll文件
我们在Model文件夹和HotFix文件夹中创建两个init脚本编译一下。 我们就可以在这里查看到我们这两个程序集了
加载unityHotFix.dll和Unity.HotFix.pdb文件
我们HotFix问价夹存放的都是我们需要用到热更新的代码,而Modle使我们初始化打包的脚本文件夹,简而言之,我们所有的代码大部分存放在HotFix,Mondle,ThirdParty文件夹中,除非有些脚本要放在Edtior问价夹中。因为这样我们可以很方便的管理脚本。
为什么加载unityHotFix.dll和Unity.HotFix.pdb文件
因为这是跨域加载,要读取热更新里面的脚本我们就要在主工程项目加载到这两个文件然后才能通过ILRuntime进行访问。
开始加载
我们首先创建一个Edtior文件夹,再创建BuildEdtior文件夹,再创建一个BuildHotFix脚本
脚本内容
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
[InitializeOnLoad]
public class BuildHotFixEditor
{
private const string scriptAssembliesDir = "Library/ScriptAssemblies";
private const string codeDir = "Assets/Res/Code/";
private const string hotfixDll="Unity.HotFix.dll";
private const string hotfixPdb = "Unity.HotFix.pdb";
static BuildHotFixEditor()
{
File.Copy(Path.Combine(scriptAssembliesDir,hotfixDll),
Path.Combine(codeDir,hotfixDll+".bytes"),true);
File.Copy(Path.Combine(scriptAssembliesDir,hotfixPdb),
Path.Combine(codeDir,hotfixPdb+".bytes"),true);
Debug.Log("复制hotfix文件成功");
}
}
这样子我们每次编译脚本后他都会自动重新修改刷新这两个文件夹的内容,保证实时性
HotFixManager脚本
下面的脚本很大我们耐心一点,不要一次性全部看完,我们慢慢来一步一步的讲解 在Model文件夹创建HotFixManager脚本,作为全局变量加载HotFix文件夹中脚本内容
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using ILRuntime.CLR.Method;
using ILRuntime.CLR.TypeSystem;
using ILRuntime.Mono.Cecil.Pdb;
using ILRuntime.Runtime.Intepreter;
using ILRuntime.Runtime.Stack;
using Unity.Model;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
using UnityEngine.Events;
using AppDomain = ILRuntime.Runtime.Enviorment.AppDomain;
public class HotFixManager : MonoBehaviour
{
public GameObject code;
private MemoryStream dllStream;
private MemoryStream pdbStream;
private AppDomain appDomain;
private string namespaceName = "HotFix";
private string className = "Test";
private string name = "";
void Start()
{
name = namespaceName + "." + className;
Load();
热更新加载函数();
}
void Load()
{
CodeReference cr = code.GetComponent<CodeReference>();
byte[] assBytes = cr.hotFixDll.bytes;
byte[] pdbBytes = cr.hotFixPdb.bytes;
#if ILRuntime
dllStream = new MemoryStream(assBytes);
pdbStream = new MemoryStream(pdbBytes);
appDomain = new AppDomain();
appDomain.LoadAssembly(dllStream,pdbStream,new PdbReaderProvider());
委托适配器();
跨域继承适配器注册();
CLR重定向方法();
Debug.Log("ILRuntime模式加载成功");
#else
Assembly.Load(assBytes,pdbBytes);
#endif
}
void 跨域继承适配器注册()
{
appDomain.RegisterCrossBindingAdaptor(new UIBaseAdapter());
}
void 热更新加载函数()
{
#region 1.加载静态无返回函数
appDomain.Invoke(name, "无参函数", null,null);
appDomain.Invoke(name,"有一参数函数", null, "皮学渣");
appDomain.Invoke(name, "有多参函数", null, new string[] {"皮学渣", "学渣皮"});
appDomain.Invoke(name, "多个不同参数类型无返回函数", null, new object[] {"皮学渣", 211});
#endregion
#region 2.加载静态有返回函数
object m1 = appDomain.Invoke(name, "无参函数有返回值", null, null);
Debug.Log(m1);
object m2 = appDomain.Invoke(name, "有一参数函数有返回", null, "皮学渣");
Debug.Log(m2);
object m3 = appDomain.Invoke(name, "有多参函数有返回", null, new string[]{"皮学渣","学渣皮"});
Debug.Log(m3);
object m4 = appDomain.Invoke(name, "多个不同参数类型无返回函数有返回", null, new object[]{"皮学渣",211985});
Debug.Log(m4);
#endregion
#region 3.加载动态无返回函数
InstantiateTest();
#endregion
#region 4.加载重载函数
IType type1 = appDomain.LoadedTypes[name];
Debug.Log(type1);
IMethod method1 = type1.GetMethod("Log", 0);
appDomain.Invoke(method1, null, null);
IType type2 = appDomain.LoadedTypes[name];
List<IType> param2 = new List<IType>();
param2.Add(appDomain.GetType(typeof(string)));
IMethod method2 = type2.GetMethod("Log", param2, null);
appDomain.Invoke(method2, null, "皮学渣");
IType type3 = appDomain.LoadedTypes[name];
List<IType> param3 = new List<IType>();
param3.Add(appDomain.GetType(typeof(string)));
param3.Add(appDomain.GetType(typeof(int)));
IMethod method3 = type2.GetMethod("Log", param3, null);
appDomain.Invoke(method3, null, new object[]{"皮学渣",211985});
#endregion
#region 5.加载成员变量
调用变量成员();
#endregion
#region 6.加载泛型函数
调用泛型();
#endregion
#region 7.加载委托
调用委托();
#endregion
#region 跨域继承的调用
调用继承类函数();
#endregion
#region CLR重定向
#endregion
}
void InstantiateTest()
{
ILTypeInstance test= appDomain.Instantiate(name, null);
appDomain.Invoke(name, "动态无参", test, null);
appDomain.Invoke(name, "动态有一参函数", test, "皮学渣");
object x1=appDomain.Invoke(name, "动态无参有返回值", test, null);
Debug.Log(x1);
object x2=appDomain.Invoke(name, "动态有一参函数有返回值", test, "皮学渣");
Debug.Log(x2) ;
}
void 调用变量成员()
{
ILTypeInstance test = appDomain.Instantiate(name);
int id1 = (int) appDomain.Invoke(name, "get_ID", test, null);
Debug.Log(id1);
appDomain.Invoke(name, "set_ID", test, 985211);
int id2 = (int) appDomain.Invoke(name, "get_ID", test, null);
Debug.Log(id2);
}
void 调用泛型()
{
ILTypeInstance test = appDomain.Instantiate(name);
appDomain.InvokeGenericMethod(name, "泛型函数",
new IType[] {appDomain.GetType(typeof(string))}, test, "皮学渣");
}
void 调用委托()
{
ILTypeInstance test = appDomain.Instantiate(name);
appDomain.Invoke(name, "ButtonClick", test, null);
appDomain.Invoke(name, "调用这些委托", test, null);
}
void 委托适配器()
{
appDomain.DelegateManager.RegisterMethodDelegate<int>();
appDomain.DelegateManager.RegisterFunctionDelegate<int,int>();
appDomain.DelegateManager.RegisterDelegateConvertor<UnityAction>
(
(act) =>
{
return new UnityAction(()=>
((Action) act)()
);
}
);
}
void 调用继承类函数()
{
string pName = "HotFix.继承unity主程序中的类";
UIBase uibase = appDomain.Instantiate<UIBase>(pName);
int id = (int)appDomain.Invoke(pName, "get_MyID", uibase, null);
Debug.Log(id);
appDomain.Invoke(pName, "HandleEvent", uibase, 50);
appDomain.Invoke(pName, "Open", uibase, "皮学渣");
}
unsafe void CLR重定向方法()
{
MethodInfo method= typeof(Debug).GetMethod("Log",new System.Type[] { typeof(object) });
appDomain.RegisterCLRMethodRedirection(method, DLog);
}
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;
StackObject* __ret = ILIntepreter.Minus(__esp, 1);
ptr_of_this_method = ILIntepreter.Minus(__esp, 1);
object message = StackObject.ToObject(ptr_of_this_method, __domain, __mStack);
__intp.Free(ptr_of_this_method);
string stackTrace = __domain.DebugService.GetStackTrace(__intp);
Debug.Log(string.Format("{0}\n----------------\n{1}", message, stackTrace));
return __ret;
}
}
Load函数
void Load()
{
CodeReference cr = code.GetComponent<CodeReference>();
byte[] assBytes = cr.hotFixDll.bytes;
byte[] pdbBytes = cr.hotFixPdb.bytes;
#if ILRuntime
dllStream = new MemoryStream(assBytes);
pdbStream = new MemoryStream(pdbBytes);
appDomain = new AppDomain();
appDomain.LoadAssembly(dllStream,pdbStream,new PdbReaderProvider());
委托适配器();
跨域继承适配器注册();
CLR重定向方法();
Debug.Log("ILRuntime模式加载成功");
#else
Assembly.Load(assBytes,pdbBytes);
#endif
}
Load函数的作用就是进行初始化appDomain,它是我们ILRuntime的集成者,我们都是通过它来访问热更新文件。 而委托适配器();跨域继承适配器注册();CLR重定向方法();这三个函数是进行适配器注册,所以我们在个热更新项目中也是在初始化的时候将所有的适配器加载出来,适配器后面会讲。
HotFix文件夹Test脚本
脚本内容
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace HotFix
{
public class Test
{
#region 函数
public Test()
{
Debug.Log("我是无参构造函数");
}
public Test(string text)
{
Debug.Log($"我是有参构造函数string text: {text}");
}
public static void 无参函数()
{
Debug.Log("我是静态无参函数无返回函数");
}
public static void 有一参数函数(string text)
{
Debug.Log("我是静态有一个string参数函数无返回函数: "+text);
}
public static void 有多参函数(string text1, string text2)
{
Debug.Log($"我是静态有多个参数无返回函数" +
$"string text1: {text1} string text2: {text2}");
}
public static void 多个不同参数类型无返回函数(string text, int m)
{
Debug.Log($"我是静态有多个不同参数类型无返回函数 " +
$" string text: {text} int m: {m}");
}
public static void Log()
{
Debug.Log("无参无返回Log函数");
}
public static void Log(string text)
{
Debug.Log($"有一个string参数无返回Log函数 text: {text}");
}
public static void Log(string text, int m)
{
Debug.Log($"有一个string参数一个int参乎上无返回Log函数 text: {text} m: {m}");
}
public static int 无参函数有返回值()
{
Debug.Log("我是静态无参函数有返回函数");
return 1;
}
public static int 有一参数函数有返回(string text)
{
Debug.Log("我是静态有一个string参数函数有返回函数: "+text);
return 2;
}
public static int 有多参函数有返回(string text1, string text2)
{
Debug.Log($"我是静态有多个参数有返回函数" +
$"string text1: {text1} string text2: {text2}");
return 3;
}
public static int 多个不同参数类型无返回函数有返回(string text, int m)
{
Debug.Log($"我是静态有多个不同参数类型有返回函数 " +
$" string text: {text} int m: {m}");
return 4;
}
public void 动态无参()
{
Debug.Log("我是动态无参无返回函数");
}
public void 动态有一参函数(string text)
{
Debug.Log("我是动态有一参数函数 string text: "+text);
}
public int 动态无参有返回值()
{
Debug.Log("我是动态无参有返回函数");
return 1;
}
public int 动态有一参函数有返回值(string text)
{
Debug.Log("我是动态有一参数有返回值函数 string text: "+text);
return 2;
}
#endregion
#region 字段变量
private int id=5000;
public int ID
{
get { return id; }
set { id = value; }
}
#endregion
#region 泛型函数
public void 泛型函数<T>(T t)
{
Debug.Log($"我是泛型函数参数是:{t}");
}
#endregion
#region UnityAction委托调用
public void ButtonClick()
{
Button button = GameObject.Find("Canvas/Test").GetComponent<Button>();
button.onClick.AddListener(OnClike);
}
private void OnClike()
{
Debug.Log("点击了Test按钮");
}
#endregion
#region 其他委托delegate Func Action
public delegate void Delegate委托();
public Action<int> action委托;
public Func<int, int> func委托;
public void 注册delegate委托()
{
Debug.Log("使用了delegate委托");
}
public void 注册Action委托(int n)
{
Debug.Log($"使用了Action委托,参数数n: {n}");
}
public int 注册Func委托(int m)
{
Debug.Log($"使用了func委托,参数数m:{m}");
return m;
}
public void 调用这些委托()
{
Delegate委托 delegate委托 = 注册delegate委托;
action委托 = 注册Action委托;
func委托 = 注册Func委托;
delegate委托();
action委托(985);
int m = func委托(985211);
Debug.Log(m);
}
#endregion
}
}
现在开始使用
跨域访问函数
我们在Test脚本中定义了好多函数,我们现在只看我划分分函数部分,所谓什么类型的都包括了,所以看完一遍你就完全可以掌握,并且不会出现什么疑问 我们在HotFixManager中进行访问 这就看我们在HotFixManager中的热更新加载函数();
#region 1.加载静态无返回函数
appDomain.Invoke(name, "无参函数", null,null);
appDomain.Invoke(name,"有一参数函数", null, "皮学渣");
appDomain.Invoke(name, "有多参函数", null, new string[] {"皮学渣", "学渣皮"});
appDomain.Invoke(name, "多个不同参数类型无返回函数", null, new object[] {"皮学渣", 211});
#endregion
#region 2.加载静态有返回函数
object m1 = appDomain.Invoke(name, "无参函数有返回值", null, null);
Debug.Log(m1);
object m2 = appDomain.Invoke(name, "有一参数函数有返回", null, "皮学渣");
Debug.Log(m2);
object m3 = appDomain.Invoke(name, "有多参函数有返回", null, new string[]{"皮学渣","学渣皮"});
Debug.Log(m3);
object m4 = appDomain.Invoke(name, "多个不同参数类型无返回函数有返回", null, new object[]{"皮学渣",211985});
Debug.Log(m4);
#endregion
#region 3.加载动态无返回函数
InstantiateTest();
#endregion
#region 4.加载重载函数
IType type1 = appDomain.LoadedTypes[name];
Debug.Log(type1);
IMethod method1 = type1.GetMethod("Log", 0);
appDomain.Invoke(method1, null, null);
IType type2 = appDomain.LoadedTypes[name];
List<IType> param2 = new List<IType>();
param2.Add(appDomain.GetType(typeof(string)));
IMethod method2 = type2.GetMethod("Log", param2, null);
appDomain.Invoke(method2, null, "皮学渣");
IType type3 = appDomain.LoadedTypes[name];
List<IType> param3 = new List<IType>();
param3.Add(appDomain.GetType(typeof(string)));
param3.Add(appDomain.GetType(typeof(int)));
IMethod method3 = type2.GetMethod("Log", param3, null);
appDomain.Invoke(method3, null, new object[]{"皮学渣",211985});
#endregion
void InstantiateTest()
{
ILTypeInstance test= appDomain.Instantiate(name, null);
appDomain.Invoke(name, "动态无参", test, null);
appDomain.Invoke(name, "动态有一参函数", test, "皮学渣");
object x1=appDomain.Invoke(name, "动态无参有返回值", test, null);
Debug.Log(x1);
object x2=appDomain.Invoke(name, "动态有一参函数有返回值", test, "皮学渣");
Debug.Log(x2) ;
}
跨域加载成员变量
HotFix.Test脚本中
#region 字段变量
private int id=5000;
public int ID
{
get { return id; }
set { id = value; }
}
#endregion
Modle.HotFixManager脚本 热更新加载函数():
#region 5.加载成员变量
调用变量成员();
#endregion
void 调用变量成员()
{
ILTypeInstance test = appDomain.Instantiate(name);
int id1 = (int) appDomain.Invoke(name, "get_ID", test, null);
Debug.Log(id1);
appDomain.Invoke(name, "set_ID", test, 985211);
int id2 = (int) appDomain.Invoke(name, "get_ID", test, null);
Debug.Log(id2);
}
跨域委托
我们在场景创建一个button按钮,接下来我们通过HotFix.Test脚本获取button并注册点击事件
HotFix.Test
#region UnityAction委托调用
public void ButtonClick()
{
Button button = GameObject.Find("Canvas/Test").GetComponent<Button>();
button.onClick.AddListener(OnClick);
}
private void OnClick()
{
Debug.Log("点击了Test按钮");
}
#endregion
#region 其他委托delegate Func Action
public delegate void Delegate委托();
public Action<int> action委托;
public Func<int, int> func委托;
public void 注册delegate委托()
{
Debug.Log("使用了delegate委托");
}
public void 注册Action委托(int n)
{
Debug.Log($"使用了Action委托,参数数n: {n}");
}
public int 注册Func委托(int m)
{
Debug.Log($"使用了func委托,参数数m:{m}");
return m;
}
public void 调用这些委托()
{
Delegate委托 delegate委托 = 注册delegate委托;
action委托 = 注册Action委托;
func委托 = 注册Func委托;
delegate委托();
action委托(985);
int m = func委托(985211);
Debug.Log(m);
}
#endregion
如果我们直接向上面那样调用函数调用ButtonClick的话会进行报错。 报错的原因就是没有进行委托适配 button.onClick.AddListener(OnClick); 这一句参数OnClick是一个UnityAction类型委托,而ILRuntime不支持UnityAction,所以我们要进行委托注册,这样才能调用UnityAction类型委托 官方文档
HotFixManager
void 委托适配器()
{
appDomain.DelegateManager.RegisterMethodDelegate<int>();
appDomain.DelegateManager.RegisterFunctionDelegate<int,int>();
appDomain.DelegateManager.RegisterDelegateConvertor<UnityAction>
(
(act) =>
{
return new UnityAction(()=>
((Action) act)()
);
}
);
}
我们进行注册后,在load函数初始化的后面进行调用
void 调用委托()
{
ILTypeInstance test = appDomain.Instantiate(name);
appDomain.Invoke(name, "ButtonClick", test, null);
appDomain.Invoke(name, "调用这些委托", test, null);
}
这个函数就是调用我们在Test脚本中的委托
跨域继承适配
当我们在热更新中的脚本要继承主工程中的类时,我们要对被继承的类进行适配,这样我们调用hotfix中派生类才能成功。
我们在Model文件夹创建一个抽象基类UIBase
UIase
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Unity.Model
{
public abstract class UIBase
{
public virtual int MyID
{
get { return 100; }
}
public virtual void Open(string text)
{
Debug.Log("UIBase中的Open方法");
}
public abstract void HandleEvent(int id);
}
}
现在再Model问价中创建Adapter文件夹,在该文件夹创建UIBaseAdapter脚本
接下来我们要根据官方文档来进行适配器的编写
using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Enviorment;
using ILRuntime.Runtime.Intepreter;
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Model;
using UnityEngine;
public class UIBaseAdapter : CrossBindingAdaptor
{
public override Type BaseCLRType
{
get
{
return typeof(UIBase);
}
}
public override Type[] BaseCLRTypes => base.BaseCLRTypes;
public override Type AdaptorType
{
get { return typeof(Adapter); }
}
public override object CreateCLRInstance(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance)
{
return new Adapter(appdomain, instance);
}
class Adapter : UIBase, CrossBindingAdaptorType
{
private ILRuntime.Runtime.Enviorment.AppDomain appdomain;
private ILTypeInstance instance;
public Adapter()
{
}
public Adapter(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance)
{
this.appdomain = appdomain;
this.instance = instance;
}
public ILTypeInstance ILInstance
{
get
{
return instance;
}
}
bool mGetMyIDGot = false;
IMethod mGetMyID;
bool isGetMyIDInvoking = false;
public override int MyID
{
get
{
if(mGetMyIDGot==false)
{
mGetMyID = instance.Type.GetMethod("get_MyID", 0);
mGetMyIDGot = true;
}
if(mGetMyID!=null&& isGetMyIDInvoking==false)
{
isGetMyIDInvoking = true;
int m=(int)appdomain.Invoke(mGetMyID, instance, null);
isGetMyIDInvoking=false;
return m;
}
return base.MyID;
}
}
IMethod mHandleEvent;
bool isHandleEventCalled = false;
object[] parame1=new object[1];
public override void HandleEvent(int id)
{
if(mHandleEvent==null)
{
mHandleEvent = instance.Type.GetMethod("HandleEvent", 1);
}
if(mHandleEvent!=null)
{
parame1[0] = id;
appdomain.Invoke(mHandleEvent, instance, parame1);
}
}
bool mOpenGot=false;
IMethod mOpen;
bool isOpenCalled = false;
object[] paream2=new object[1];
public override void Open(string text)
{
if(mOpenGot==false)
{
mOpen = instance.Type.GetMethod("Open", 1);
}
if(mOpen!=null&&isOpenCalled==false)
{
isOpenCalled = true;
paream2[0]=text;
appdomain.Invoke(mOpen, instance, paream2);
isOpenCalled=false;
}
else
{
base.Open(text);
}
}
}
}
在 HotFixManager 脚本
void 跨域继承适配器注册()
{
appDomain.RegisterCrossBindingAdaptor(new UIBaseAdapter());
}
在Load函数下面添加进行适配 现在我们添加了委托适配,跨域继承适配
HotFix文件夹创建 继承unity主程序中的类脚本
using System.Collections;
using System.Collections.Generic;
using Unity.Model;
using UnityEngine;
namespace HotFix
{
public class 继承unity主程序中的类 : UIBase
{
public override int MyID => 985211;
public override void HandleEvent(int id)
{
Debug.Log("现在是调用了重写的HandleEvent方法参数t是:" + id);
}
public override void Open(string text)
{
Debug.Log($"现在是调用了重写的Open方法参数text:{text}");
}
}
}
HotFixManager
void 调用继承类函数()
{
string pName = "HotFix.继承unity主程序中的类";
UIBase uibase = appDomain.Instantiate<UIBase>(pName);
int id = (int)appDomain.Invoke(pName, "get_MyID", uibase, null);
Debug.Log(id);
appDomain.Invoke(pName, "HandleEvent", uibase, 50);
appDomain.Invoke(pName, "Open", uibase, "皮学渣");
}
CLR重定向
我的理解重定向就是重新定义一个类和方法 接下来我们重定向Debug.Log方法
HotFixManager
unsafe void CLR重定向方法()
{
MethodInfo method= typeof(Debug).GetMethod("Log",new System.Type[] { typeof(object) });
appDomain.RegisterCLRMethodRedirection(method, DLog);
}
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;
StackObject* __ret = ILIntepreter.Minus(__esp, 1);
ptr_of_this_method = ILIntepreter.Minus(__esp, 1);
object message = StackObject.ToObject(ptr_of_this_method, __domain, __mStack);
__intp.Free(ptr_of_this_method);
string stackTrace = __domain.DebugService.GetStackTrace(__intp);
Debug.Log(string.Format("{0}\n----------------\n{1}", message, stackTrace));
return __ret;
}
然后在Load函数是下面添加该函数
运行各种的结果
点击Button按钮
大总结
希望我这篇文章能够让你快速入门
|