Mono.Cecil使用示例之获取源文件路径

使用VS在Debug模式下生成Dll工程时都会生成对应的pdb调试文件,pdb调试文件中记录了源文件路径,行号等信息。而Unity使用mono的mdb格式的调试文件,将dll和对应的pdb调试文件拷到Assets目录下,Unity会生成对应的mdb调试文件。也可以在Unity安装目录中找到pdb2mdb程序,可以将vs生成的pdb文件转换成mdb文件,不过在windows环境下或者高版本的vs生成的pdb文件,使用该工具转换会有很多问题,可以参考下面链接解决

http://www.robinryf.com/blog/2016/04/10/convert-pdb-to-mdb.html

 

现在我们有了Dll程序集文件和对应的mdb调试文件,如果获取到了一个定义在该程序集中的类型,如何获取到定义该类型所在的源文件呢

查看Mono.Cecil提供的API可以发现,在函数定义类MethodDefinition中的函数体属性MethodBody中的每条IL代码结构Instruction中都有个SequencePoint属性,SequencePoint类中的Document属性中的Url属性记录了源文件的路径

 

实现步骤

1.      读取Dll和对应的调试文件

设要读取的Dll文件路径为string dllPath;

var readPar = new ReaderParameters { ReadSymbols = true };

AssemblyDefinition ad = AssemblyDefinition.ReadAssembly(dllPath, readPar);

这里调用的上几个示例中读取程序集函数的重载版本,加了一个是否读取调试文件的参数,该参数为true时,读取程序集时会在程序集所在的文件夹下查找同名的.mdb或.pdb调试文件并加载

2.      读取指定类型

设要读取的类型为Type type;

TypeDefinition tr = ad.MainModule.GetType(type.FullName);

3. 获取源文件路径

设源文件路径string scriptPath = "";

foreach (var method in tr.Methods)

        {

            if (method.HasBody)

            {

                var body = method.Body;

                foreach (var il in body.Instructions)

                {

                    if (il.SequencePoint != null)

                    {

                        scriptPath = il.SequencePoint.Document.Url;

                        break;

                    }

                }

            }

            if (!string.IsNullOrEmpty(scriptPath))

            {

                break;

            }

    }

 

该方法在类型中没有函数和属性时无法获取到对应的源文件路径

可以将上面的代码封装成函数

public static string GetDllScriptPath(string dllPath,Type type)

{···}

 

使用示例

public class DllScriptPathTest

{

[MenuItem("CONTEXT/MonoBehaviour/OpenScript", false, 606)]

    static void OpenScript(MenuCommand command)

    {

MonoScript script = MonoScript.FromMonoBehaviour(command.context as MonoBehaviour);

        string path = AssetDatabase.GetAssetPath(script);

 

        if (path.ToLower().EndsWith(".cs"))

        {

            AssetDatabase.OpenAsset(script);

            return;

        }

 

Type t = command.context.GetType();

        string scriptPath = GetDllScriptPath(path, t);

        if(File.Exists(scriptPath))

        {

            Debug.Log(scriptPath);

        }

}

 

    [OnOpenAssetAttribute]

    static bool OnOpenScript(int instanceID, int line)

    {

        Object asset = EditorUtility.InstanceIDToObject(instanceID);

        if (asset == null || asset.GetType() != typeof(MonoScript))

        {

            return false;

        }

 

        Type type = (asset as MonoScript).GetClass();

 

        string assetPath = AssetDatabase.GetAssetPath(instanceID);

        string scriptPath = GetDllScriptPath(assetPath, type);

        if (File.Exists(scriptPath))

        {

            Debug.Log(scriptPath);

            return true;

        }

 

        return false;

    }

}

该示例在双击Dll脚本时或在Dll脚本组件右上方点击菜单“Open Script”时,会将Dll脚本对应的cs源文件路径打印到了日志窗口上

Mono.Cecil使用示例之获取源文件路径_第1张图片


其中带有MenuItem("CONTEXT/MonoBehaviour/OpenScript", false, 606)特性的静态函数给所有继承自MonoBehaviour的组件添加了菜单Open Script,参见

https://docs.unity3d.com/ScriptReference/MenuItem.html

 

其中带有OnOpenAssetAttribute特性的静态函数在Unity中打开文件时被Unity调用,参见

https://docs.unity3d.com/ScriptReference/Callbacks.OnOpenAssetAttribute.html

你可能感兴趣的:(unity工作日记,Unity,Mono.Cecil,调试)