在Unity中利用Mono.Cecil将代码注入到Dll中
来源:互联网 发布:网络表格怎么加斜线 编辑:程序博客网 时间:2024/05/19 10:15
转载自我的小站:原文地址
通过Mono.Cecil我们可以通过Emit的方式将代码注入到已有的dll中,以实现AOP等高级功能。
Unity的代码在修改之后会自动编译到Library\ScriptAssemblies下的两个Assembly中,所以我会尝试着将代码注入到其中。
public class Test : MonoBehaviour{void Start(){ InjectMod();}void InjectMod () { Debug.Log("Heihei asdasd");}}
将Test绑定到场景物体上,运行后我们会发现输出“Heihei asdasd”,就像我们预期的一样。
然后我们尝试着将代码注入到该函数中。
private static bool hasGen = false;[PostProcessBuild(1000)]private static void OnPostprocessBuildPlayer(BuildTarget buildTarget, string buildPath){ hasGen = false;}[PostProcessScene]public static void TestInjectMothodOnPost(){ if (hasGen == true) return; hasGen = true; TestInjectMothod();}[InitializeOnLoadMethod]public static void TestInjectMothod(){ var assembly = AssemblyDefinition.ReadAssembly(@"D:\Documents\Unity5Projects\UnityDllInjector\Library\ScriptAssemblies\Assembly-CSharp.dll"); var types = assembly.MainModule.GetTypes(); foreach(var type in types) { foreach(var Method in type.Methods) { if(Method.Name == "InjectMod") { InjectMethod(Method, assembly); } } } var writerParameters = new WriterParameters { WriteSymbols = true }; assembly.Write(@"D:\Documents\Unity5Projects\UnityDllInjector\Library\ScriptAssemblies\Assembly-CSharp.dll", new WriterParameters());}
我们首先看TestInjectMothod,这是我们在编辑器下进行注入的函数,这里我们需要注意的是,每当我们修改代码之后我们注入的结果会被覆盖掉,所以我们在每次修改代码之后都需要进行注入,所以我们这里添加了标签:InitializeOnLoadMethod
这个标签的意思是,当初始化的时候都进行执行,所以编译完成之后就会自动执行。
然后我们看前面两个函数,这两个函数是为了在打包时进行注入而存在的,其中hasGen是为了防止重复注入而定义的flag。
然后我们查看一下我们的注入方法:
private static void InjectMethod(MethodDefinition method, AssemblyDefinition assembly){ var firstIns = method.Body.Instructions.First(); var worker = method.Body.GetILProcessor(); //获取Debug.Log方法引用 var hasPatchRef = assembly.MainModule.Import( typeof(Debug).GetMethod("Log", new Type[] { typeof(string) })); //插入函数 var current = InsertBefore(worker, firstIns, worker.Create(OpCodes.Ldstr, "Inject")); current = InsertBefore(worker, firstIns, worker.Create(OpCodes.Call, hasPatchRef)); //计算Offset ComputeOffsets(method.Body);}
在这个函数中我们可以看到,我们首先将我们所需要的函数导入,然后插入到方法的最前端。
会用到的一些工具函数
/// <summary>/// 语句前插入Instruction, 并返回当前语句/// </summary>private static Instruction InsertBefore(ILProcessor worker, Instruction target, Instruction instruction){ worker.InsertBefore(target, instruction); return instruction;}/// <summary>/// 语句后插入Instruction, 并返回当前语句/// </summary>private static Instruction InsertAfter(ILProcessor worker, Instruction target, Instruction instruction){ worker.InsertAfter(target, instruction); return instruction;}//计算注入后的函数偏移值private static void ComputeOffsets(MethodBody body){ var offset = 0; foreach (var instruction in body.Instructions) { instruction.Offset = offset; offset += instruction.GetSize(); }}
等待编译完成,并且运行程序,我们发现在输出原来的语句之前多了一句“Inject”
可是我们在查看代码的时候并没有发生任何改变,这是因为我们只修改了dll而并非修改源代码。
通过反编译软件ILSpy我们可以通过IL来反编译出我们的dll中的语句。
代码变为:
public class Test : MonoBehaviour{ private void Start() { this.InjectMod(); } private void InjectMod() { Debug.Log("Inject"); Debug.Log("Heihei asdasd"); }}
注入成功,也达成了我们的目的。
这个东西到底有什么用呢?
之前在看知乎上的一篇文章,slua的作者分析了一下腾讯最近xlua的思路,也就是luapatch。
大概就是在每一个需要热补丁的函数前面加上一个[hotfix]就可以通过热更新lua代码来进行热补丁。
这是一种非侵入式的方法来为我们的框架添加额外的功能,这类似于AOP。
例如,原来我们的代码是
[hotfix]void Test(){ //DoSomething}
在注入之后就变成了
[hotfix]void Test(){ //如果存在热补丁 if(hasPatch()){ //加载luaPatch并且执行 return; } //DoSomething}
也就是通过lua完全替代了原本的函数。
如果是手动添加这些代码的话想必是一个不小的工作量,但是如果使用了我们以上所写的方式来做这个东西则会轻松非常多。
也就是不侵入代码的情况下自动注入我们想要的额外代码。
或许在不久的将来会出现不少基于此类的框架,就像Java中的Spring等等。虽然C#的代码生成方式比起Java来说要麻烦不少,但是也是可以做的!
在公司的IOS版本中,我也想加入这样的方法来进行框架的构筑,而并非热更新而已。
以上。
本文章参考了:
http://www.jianshu.com/p/481994e8b7df
https://www.zhihu.com/question/54344452/answer/138990189
感谢大大们的分享,让我这样的小透明也可以不断学习到新的技术。
顺便给大家拜个晚年吧!
- 在Unity中利用Mono.Cecil将代码注入到Dll中
- 在Unity中使用动态库(Mono DLL)
- 【Unity基础知识之五】Unity3d-在Unity项目中使用Mono DLL(动态链接库)
- 编译时MSIL注入--实践Mono Cecil
- 使用Mono.Cecil对MSIL进行注入
- 使用Mono.Cecil对MSIL进行注入
- 使用Mono.Cecil对MSIL进行注入
- Unity3d-在Unity项目中使用Mono DLL(动态链接库)
- 利用远程线程将代码注入到目标进程中执行
- 利用远程线程将代码注入到目标进程中执行
- 将托管dll注入到非托管进程中
- 将托管dll注入到非托管进程中
- 在Unity中调试dll
- 远程将DLL注入应用程序中
- 编译时MSIL注入--实践Mono Cecil(1)
- 编译时MSIL注入--实践Mono Cecil(1)
- 将Mono嵌入到自己的程序中
- [转帖]将连接数据库的代码隐藏在DLL中
- Win7设置共享文件夹
- spring 配置xml文件
- Codeforces Round #396 (Div. 2)C. Mahmoud and a Message
- Unity开发必备电脑快捷键及电脑知识
- HCIE知识整理,简单说说IGMPV1 V2 V3
- 在Unity中利用Mono.Cecil将代码注入到Dll中
- docker
- 最好的语言 PHP + 最好的前端测试框架 Selenium = 最好的爬虫(下)
- codeforces766D Mahmoud and a Dictionary [并查集]【数据结构】
- leetcode 387 First Unique Character in a String
- WC2017游记
- Map.Entry简介、遍历HashMap
- vue项目提交到github
- UITableViewCell 原生左滑菜单使用