美文网首页unity
[Unity 3d] unity_cecil_example(元

[Unity 3d] unity_cecil_example(元

作者: 雨落随风 | 来源:发表于2019-07-12 23:51 被阅读3次

    教你如何使用 Mono.Cecil 进行元数据编程。

    GitHub 上的工程多如繁星,有些好的仓库,但凡不经意间错过了就很难找回,故稍作采撷,希望能帮助到有心人。

    简介:

    笔者今天推荐的仓库叫 unity_cecil_example
    Proof of concept to show how Mono.Cecil can be used with Unity for metaprogramming
    验证使用Mono.Cecil在Unity进行元数据编程的可行性。

    功能:

    For the purposes of this blog post, I'll just create a simple post processor that adds logging in and out of functions which are marked with a special attribute. A tool like this could be used just for debug builds, but stripped out of release. In his talk, JB Evain showed some interesting examples like inserting sanity checks on function parameters, amongst other things.

    这个仓库作者借助 Mono.Cecil 在Unity 中通过不修改 C#源代码而直接将逻辑插入 Dll 中!需要插入逻辑的方法只需要冠上一个特定的属性即可:[Log] .

    使用:

    • 使用属性标记需要插入逻辑的方法。
    using UnityEngine;
    using System.Collections;
    
    public class Test : MonoBehaviour 
    {
        private void Start()
        {
            this.LogTest();
        }
    
        [Log] //写一个自定义的特定属性标记这个方法会被插入逻辑
        private void LogTest()
        {
            Debug.Log( "Here's some logic" );
        }
    }
    
    • 使用 [InitializaOnLoad] 属性标记,以确保每次刷新Project时遍历工程编译ok 的DLL以修改,下面是修改逻辑:
    [InitializeOnLoad]
    public static class AssemblyPostProcessor
    {
        static AssemblyPostProcessor()
        {
            try
            {
                // 锁定程序集避免被其它地方意外加载
                EditorApplication.LockReloadAssemblies();
    
                foreach( System.Reflection.Assembly assembly in AppDomain.CurrentDomain.GetAssemblies() )
                {
                    // 仅仅处理工程引用或者编译好的 dll (这是DLL编译后存放的路径)
                    if( assembly.Location.Replace( '\\', '/' ).StartsWith( Application.dataPath.Substring( 0, Application.dataPath.Length - 7 ) ) )
                    {
                        AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly( assembly.Location );
                        AssemblyPostProcessor.PostProcessAssembly( assemblyDefinition ); //此处调用后期处理逻辑
                    }
                }
                // 当上述处理搞定了就解锁程序集
                EditorApplication.UnlockReloadAssemblies();
            }
            catch( Exception e )
            {
                Debug.LogWarning( e );
            }
        }
    }
    
    • 那么核心来了,这里演示怎么利用 Cecil 修改元数据以达到修改Dll。
    MethodReference logMethodReference = moduleDefinition.Import( typeof( Debug ).GetMethod( "Log", new Type[] { typeof( object ) } ) );
    
    ILProcessor ilProcessor = methodDefinition.Body.GetILProcessor();
    
    Instruction first = methodDefinition.Body.Instructions[0];
    ilProcessor.InsertBefore( first, Instruction.Create( OpCodes.Ldstr, 
    "Enter " + typeDefinition.FullName + "." + methodDefinition.Name ) );
    ilProcessor.InsertBefore( first, Instruction.Create( OpCodes.Call, logMethodReference ) );
    
    Instruction last = methodDefinition.Body.Instructions[
    methodDefinition.Body.Instructions.Count - 1];
    ilProcessor.InsertBefore( last, Instruction.Create( OpCodes.Ldstr, 
    "Exit " + typeDefinition.FullName + "." + methodDefinition.Name ) );
    ilProcessor.InsertBefore( last, Instruction.Create( OpCodes.Call, logMethodReference ) );
    

    Tips : 所有代码仅为片段截取,所以有兴趣的请务必Down下来跑跑。

    演示:

    • 源代码在上面,所以下面仅仅展示了 IL 代码,这也是修改前的数据。


    • 这是修改后的 il 数据


    • 转为 C# 代码就可见我们插入的逻辑啦


    链接:

    Proof of concept to show how Mono.Cecil can be used with Unity for metaprogramming

    结语:

    目前该作者只是简单的在原逻辑前后各插入了一段Debug.log,但是在实际生产中还可以开开脑洞高搞事情,譬如雨凇Mo就用它做性能检查,就问酷不酷!

    扩展阅读:

    Integrating Mono.Cecil with Unity — Coder's Block - Game Development, Netcode, C++, Tea
    Unity3D研究院自动注入代码统计每个函数的执行效率以及内存分配(九十八) | 雨松MOMO程序研究院

    本文集持续更新ing,喜欢记得点赞关注哦!

    相关文章

      网友评论

        本文标题:[Unity 3d] unity_cecil_example(元

        本文链接:https://www.haomeiwen.com/subject/koeukctx.html