Mono.Cecil使用示例之给UnityEditor.dll中的ConsoleWindow添加双击委托

本文介绍如何在Unity的Console窗口中添加双击事件监听,通过修改UnityEditor.dll中的ConsoleWindow类来实现自定义的双击行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

该示例将在上例的基础上给UnityEditor.dll中的ConsoleWindow添加双击委托
UnityConsole窗口是查看日志的窗口,我们希望在Console窗口中双击某条日志时获得通知,该窗口对应的实现代码在UnityEditor.dll中的ConsoleWindow
使用ILSpy反编译UnityEditor.dll中的ConsoleWindow类,可以看到其双击判断在OnGUI函数中,并在双击时调用了LogEntries.RowGotDoubleClicked函数。
我们可以使用Mono.CecilConsoleWindow类添加一个静态委托
public static Action<int> onDoubleClick;
添加一个委托执行函数
void OnDoubleClick(int row)
{
    
if (ConsoleWindow.onDoubleClick != null)
    {
        ConsoleWindow.
onDoubleClick(row);
    }
}
并在OnGUI函数中调用LogEntries.RowGotDoubleClicked函数的后面调用OnDoubleClick函数

实现步骤
  1. 读取UnityEditor.dll程序集
在上例解决保存报错的基础上封装一个读取程序集的函数
public static AssemblyDefinition GetAssemblyDefination(string assemblyPath)
    {
        AssemblyDefinition ad = AssemblyDefinition.ReadAssembly(assemblyPath);
DefaultAssemblyResolver dar = ad.MainModule.AssemblyResolver as DefaultAssemblyResolver;
        if (dar != null)
        {
            dar.AddSearchDirectory(Path.GetDirectoryName(assemblyPath));
        }
        return ad;
}
         则读取UnityEditor.dll程序集的代码如下
         string unityEditorPath = typeof(AssetDatabase).Module.FullyQualifiedName;
     AssemblyDefinition ad = GetAssemblyDefination(unityEditorPath);
  1. 查找ConsoleWindow类
var type = ad.MainModule.Types.First(t => t.Name == "ConsoleWindow");
  1. ConsoleWindow类添加一个类型为Action<int>的静态公有委托onDoubleClick
var action = new FieldDefinition("onDoubleClick", Mono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.Static, ad.MainModule.Import(typeof(Action<int>)));
type.Fields.Add(action);
  1. ConsoleWindow类添加一个无返回值一个int类型形参row的函数OnDoubleClick
var method = new MethodDefinition("OnDoubleClick", Mono.Cecil.MethodAttributes.Public, ad.MainModule.TypeSystem.Void);
method.Parameters.Add(new ParameterDefinition("row", Mono.Cecil.ParameterAttributes.None, ad.MainModule.TypeSystem.Int32));
type.Methods.Add(method);
  1. OnDoubleClick函数添加函数体
var ilProcessor = method.Body.GetILProcessor();
var il1 = ilProcessor.Create(OpCodes.Ldsfld, action);
var il2 = ilProcessor.Create(OpCodes.Callvirt, ad.MainModule.Import(typeof(Action<int>).GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public)));
method.Body.Instructions.Add(il1);
method.Body.Instructions.Add(il1);
method.Body.Instructions.Add(ilProcessor.Create(OpCodes.Ldarg_1));
method.Body.Instructions.Add(il2);
method.Body.Instructions.Add(ilProcessor.Create(OpCodes.Ret));
ilProcessor.InsertAfter(method.Body.Instructions[1], ilProcessor.Create(OpCodes.Brfalse, method.Body.Instructions.Last()));
  1. OnGUI函数中调用LogEntries.RowGotDoubleClicked函数的后面添加对OnDoubleClick函数的调用
var ongui = type.Methods.First(m => m.Name == "OnGUI");
    var onguiProcess = ongui.Body.GetILProcessor();
    for (int i = 0; i < ongui.Body.Instructions.Count; i++)
    {
        if (ongui.Body.Instructions[i].ToString().Contains("RowGotDoubleClicked"))
        {
            onguiProcess.InsertAfter(ongui.Body.Instructions[i], onguiProcess.Create(OpCodes.Call, method));
            onguiProcess.InsertAfter(ongui.Body.Instructions[i], ongui.Body.Instructions[i - 1]);
            onguiProcess.InsertAfter(ongui.Body.Instructions[i], ongui.Body.Instructions[i - 2]);
            onguiProcess.InsertAfter(ongui.Body.Instructions[i], ongui.Body.Instructions[i - 3]);
            onguiProcess.InsertAfter(ongui.Body.Instructions[i], ongui.Body.Instructions[i - 3]);
            i += 5;
        }
}
  1. 保存修改后的程序集
ad.Write(Application.dataPath + "/../UnityEditor.dll");
其中第5,6步骤中对IL代码的操作需要对C#编译为对应的IL代码非常熟悉,否则插错一条IL代码可能使整个程序集无法编译通过甚至使整个程序崩溃。但是如何保证5,6步骤中插入的IL代码的正确性呢
一般IL代码都是由.Net系的编程语言编译生成的,只要编译通过,IL代码一般不会发生错误,所以要保证对IL代码操作的正确性,也需要借助到编译器的编译
上述第5,第6步骤对IL代码的操作可以通过下面的步骤保证其正确性
  1. 保存反编译出来的ConsoleWindow
  2. 将保存的类导进Unity中,修改使其可以通过编译
  3. 为修改后的类添加委托。委托执行函数,修改OnGUI函数等等
  4. 在工程目录\Library\ScriptAssemblies\下找到Assembly-CSharp-Editor.dll文件,在ILSpy中打开,反编译其中修改过的ConsoleWindow类为IL代码
  5. 反编译UnityEditor.dll中的ConsoleWindow类为IL代码,和第4步骤中的IL代码对比,就可以在关键部位逐条插入多出来的IL代码,或者直接替换对应的IL代码
    如果在ILSpy中可以成功反编译修改后的程序集为C#代码,说明对IL代码的修改操作成功了

与上个示例一样,关掉Unityvs,用修改后的UnityEditor替换工程目录\Library\UnityAssemblies\UnityEditor.dllUnity安装目录\Editor\Data\Managed\UnityEditor.dll
再打开工程就会发现可以在代码中注册ConsoleWindow. onDoubleClick委托了

使用示例
public class ConsoleActionTest
{
    [InitializeOnLoadMethod]
    static void Init()
    {
        ConsoleWindow.onDoubleClick -= OnConsoleDoubleClick;
        ConsoleWindow.onDoubleClick += OnConsoleDoubleClick;
    }
    static void OnConsoleDoubleClick(int row)
    {
        LogEntry log = new LogEntry();
        LogEntries.GetEntryInternal(row, log);
        Debug.Log(log.file);
        Debug.Log(log.line);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值