Mono.Cecil使用示例之给UnityEditor.dll中的ConsoleWindow添加双击委托
2017-08-22 18:17
363 查看
[align=justify]该示例将在上例的基础上给UnityEditor.dll中的ConsoleWindow添加双击委托[/align]
[align=justify]Unity的Console窗口是查看日志的窗口,我们希望在Console窗口中双击某条日志时获得通知,该窗口对应的实现代码在UnityEditor.dll中的ConsoleWindow类[/align]
[align=justify]使用ILSpy反编译UnityEditor.dll中的ConsoleWindow类,可以看到其双击判断在OnGUI函数中,并在双击时调用了LogEntries.RowGotDoubleClicked函数。[/align]
[align=justify]我们可以使用Mono.Cecil给ConsoleWindow类添加一个静态委托[/align]
public
static
Action<int>
onDoubleClick;
[align=justify]添加一个委托执行函数[/align]
void
OnDoubleClick(int
row)
{
if
(ConsoleWindow.onDoubleClick !=
null)
{
ConsoleWindow.onDoubleClick(row);
}
}
并在OnGUI函数中调用LogEntries.RowGotDoubleClicked函数的后面调用OnDoubleClick函数
[align=justify]实现步骤[/align]
[align=justify]读取UnityEditor.dll程序集[/align]
在上例解决保存报错的基础上封装一个读取程序集的函数
public
static
AssemblyDefinition
GetAssemblyDefination(string
assemblyPath)
{<
2054b
/span>
AssemblyDefinition
ad
=
AssemblyDefinition.ReadAssembly(assemblyPath);
DefaultAssemblyResolver
dar
=
ad.MainModule.AssemblyResolver
as
DefaultAssemblyResolver;
if
(dar
!=
null)
[align=left] {[/align]
dar.AddSearchDirectory(Path.GetDirectoryName(assemblyPath));
[align=left] }[/align]
return
ad;
}
[align=justify] 则读取UnityEditor.dll程序集的代码如下[/align]
string
unityEditorPath
=
typeof(AssetDatabase).Module.FullyQualifiedName;
AssemblyDefinition
ad
=
GetAssemblyDefination(unityEditorPath);
[align=justify]查找ConsoleWindow类[/align]
var
type
=
ad.MainModule.Types.First(t
=>
t.Name
==
"ConsoleWindow");
[align=justify]给ConsoleWindow类添加一个类型为Action<int>的静态公有委托onDoubleClick[/align]
var
action
=
new
FieldDefinition("onDoubleClick",
Mono.Cecil.FieldAttributes.Public
|
Mono.Cecil.FieldAttributes.Static,
ad.MainModule.Import(typeof(Action<int>)));
type.Fields.Add(action);
[align=justify]给ConsoleWindow类添加一个无返回值一个int类型形参row的函数OnDoubleClick[/align]
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);
给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()));
在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++)
[align=left] {[/align]
if
(ongui.Body.Instructions[i].ToString().Contains("RowGotDoubleClicked"))
[align=left] {[/align]
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;
[align=left] }[/align]
}
保存修改后的程序集
ad.Write(Application.dataPath
+
"/../UnityEditor.dll");
其中第5,第6步骤中对IL代码的操作需要对C#编译为对应的IL代码非常熟悉,否则插错一条IL代码可能使整个程序集无法编译通过甚至使整个程序崩溃。但是如何保证5,6步骤中插入的IL代码的正确性呢
一般IL代码都是由.Net系的编程语言编译生成的,只要编译通过,IL代码一般不会发生错误,所以要保证对IL代码操作的正确性,也需要借助到编译器的编译
[align=justify]上述第5,第6步骤对IL代码的操作可以通过下面的步骤保证其正确性[/align]
[align=justify]保存反编译出来的ConsoleWindow类[/align]
[align=justify]将保存的类导进Unity中,修改使其可以通过编译[/align]
[align=justify]为修改后的类添加委托。委托执行函数,修改OnGUI函数等等[/align]
[align=justify]在工程目录\Library\ScriptAssemblies\下找到Assembly-CSharp-Editor.dll文件,在ILSpy中打开,反编译其中修改过的ConsoleWindow类为IL代码[/align]
[align=justify]反编译UnityEditor.dll中的ConsoleWindow类为IL代码,和第4步骤中的IL代码对比,就可以在关键部位逐条插入多出来的IL代码,或者直接替换对应的IL代码[/align]
[align=justify]如果在ILSpy中可以成功反编译修改后的程序集为C#代码,说明对IL代码的修改操作成功了[/align]
[align=justify]与上个示例一样,关掉Unity和vs,用修改后的UnityEditor替换工程目录\Library\UnityAssemblies\UnityEditor.dll和Unity安装目录\Editor\Data\Managed\UnityEditor.dll[/align]
再打开工程就会发现可以在代码中注册ConsoleWindow. onDoubleClick委托了
[align=left]使用示例[/align]
public
class
ConsoleActionTest
[align=left]{[/align]
[align=left] [InitializeOnLoadMethod][/align]
static
void
Init()
[align=left] {[/align]
ConsoleWindow.onDoubleClick
-=
OnConsoleDoubleClick;
ConsoleWindow.onDoubleClick
+=
OnConsoleDoubleClick;
[align=left] }[/align]
static
void
OnConsoleDoubleClick(int
row)
[align=left] {[/align]
LogEntry
log
=
new
LogEntry();
LogEntries.GetEntryInternal(row,
log);
Debug.Log(log.file);
Debug.Log(log.line);
[align=left] }[/align]
[align=left]}[/align]
[align=justify]Unity的Console窗口是查看日志的窗口,我们希望在Console窗口中双击某条日志时获得通知,该窗口对应的实现代码在UnityEditor.dll中的ConsoleWindow类[/align]
[align=justify]使用ILSpy反编译UnityEditor.dll中的ConsoleWindow类,可以看到其双击判断在OnGUI函数中,并在双击时调用了LogEntries.RowGotDoubleClicked函数。[/align]
[align=justify]我们可以使用Mono.Cecil给ConsoleWindow类添加一个静态委托[/align]
public
static
Action<int>
onDoubleClick;
[align=justify]添加一个委托执行函数[/align]
void
OnDoubleClick(int
row)
{
if
(ConsoleWindow.onDoubleClick !=
null)
{
ConsoleWindow.onDoubleClick(row);
}
}
并在OnGUI函数中调用LogEntries.RowGotDoubleClicked函数的后面调用OnDoubleClick函数
[align=justify]实现步骤[/align]
[align=justify]读取UnityEditor.dll程序集[/align]
在上例解决保存报错的基础上封装一个读取程序集的函数
public
static
AssemblyDefinition
GetAssemblyDefination(string
assemblyPath)
{<
2054b
/span>
AssemblyDefinition
ad
=
AssemblyDefinition.ReadAssembly(assemblyPath);
DefaultAssemblyResolver
dar
=
ad.MainModule.AssemblyResolver
as
DefaultAssemblyResolver;
if
(dar
!=
null)
[align=left] {[/align]
dar.AddSearchDirectory(Path.GetDirectoryName(assemblyPath));
[align=left] }[/align]
return
ad;
}
[align=justify] 则读取UnityEditor.dll程序集的代码如下[/align]
string
unityEditorPath
=
typeof(AssetDatabase).Module.FullyQualifiedName;
AssemblyDefinition
ad
=
GetAssemblyDefination(unityEditorPath);
[align=justify]查找ConsoleWindow类[/align]
var
type
=
ad.MainModule.Types.First(t
=>
t.Name
==
"ConsoleWindow");
[align=justify]给ConsoleWindow类添加一个类型为Action<int>的静态公有委托onDoubleClick[/align]
var
action
=
new
FieldDefinition("onDoubleClick",
Mono.Cecil.FieldAttributes.Public
|
Mono.Cecil.FieldAttributes.Static,
ad.MainModule.Import(typeof(Action<int>)));
type.Fields.Add(action);
[align=justify]给ConsoleWindow类添加一个无返回值一个int类型形参row的函数OnDoubleClick[/align]
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);
给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()));
在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++)
[align=left] {[/align]
if
(ongui.Body.Instructions[i].ToString().Contains("RowGotDoubleClicked"))
[align=left] {[/align]
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;
[align=left] }[/align]
}
保存修改后的程序集
ad.Write(Application.dataPath
+
"/../UnityEditor.dll");
其中第5,第6步骤中对IL代码的操作需要对C#编译为对应的IL代码非常熟悉,否则插错一条IL代码可能使整个程序集无法编译通过甚至使整个程序崩溃。但是如何保证5,6步骤中插入的IL代码的正确性呢
一般IL代码都是由.Net系的编程语言编译生成的,只要编译通过,IL代码一般不会发生错误,所以要保证对IL代码操作的正确性,也需要借助到编译器的编译
[align=justify]上述第5,第6步骤对IL代码的操作可以通过下面的步骤保证其正确性[/align]
[align=justify]保存反编译出来的ConsoleWindow类[/align]
[align=justify]将保存的类导进Unity中,修改使其可以通过编译[/align]
[align=justify]为修改后的类添加委托。委托执行函数,修改OnGUI函数等等[/align]
[align=justify]在工程目录\Library\ScriptAssemblies\下找到Assembly-CSharp-Editor.dll文件,在ILSpy中打开,反编译其中修改过的ConsoleWindow类为IL代码[/align]
[align=justify]反编译UnityEditor.dll中的ConsoleWindow类为IL代码,和第4步骤中的IL代码对比,就可以在关键部位逐条插入多出来的IL代码,或者直接替换对应的IL代码[/align]
[align=justify]如果在ILSpy中可以成功反编译修改后的程序集为C#代码,说明对IL代码的修改操作成功了[/align]
[align=justify]与上个示例一样,关掉Unity和vs,用修改后的UnityEditor替换工程目录\Library\UnityAssemblies\UnityEditor.dll和Unity安装目录\Editor\Data\Managed\UnityEditor.dll[/align]
再打开工程就会发现可以在代码中注册ConsoleWindow. onDoubleClick委托了
[align=left]使用示例[/align]
public
class
ConsoleActionTest
[align=left]{[/align]
[align=left] [InitializeOnLoadMethod][/align]
static
void
Init()
[align=left] {[/align]
ConsoleWindow.onDoubleClick
-=
OnConsoleDoubleClick;
ConsoleWindow.onDoubleClick
+=
OnConsoleDoubleClick;
[align=left] }[/align]
static
void
OnConsoleDoubleClick(int
row)
[align=left] {[/align]
LogEntry
log
=
new
LogEntry();
LogEntries.GetEntryInternal(row,
log);
Debug.Log(log.file);
Debug.Log(log.line);
[align=left] }[/align]
[align=left]}[/align]
相关文章推荐
- Mono.Cecil使用示例之使指定程序集成为UnityEditor.dll的友元程序集
- Mono.Cecil使用示例之获取源文件路径
- JavaScript 从入门到放弃(一)事件委托和使用innerHTML添加元素
- 演示事件(Event)怎样使用以及怎样为用户控件添加一个事件(示例代码下载)
- 使用Mono.Cecil对MSIL进行注入
- mfc使用CListCtrl添加右键菜单及双击(以默认方式)打开文件。
- 使用python在wordpress博客网站添加新文章示例
- 使用Mono.Cecil改变访问级别
- C#匿名委托和Java匿名局部内部类使用方法示例
- 给ListBox添加双击事件示例代码
- 演示事件(Event)怎样使用以及怎样为用户控件添加一个事件(示例代码下载)
- 使用 STM8L DISCOVERY 示例程序添加库文件出现 Error[Li005]: no definition for XXX 的解决办法
- 演示事件(Event)怎样使用以及怎样为用户控件添加一个事件(示例代码下载)
- python使用PIL给图片添加文字生成海报示例
- 使用MOno Cecil 的相关开源项目
- 使用 STM8L DISCOVERY 示例程序添加库文件出现 Error[Li005]: no definition for XXX 的解决办法
- 把TextView中的文字添加阴影效果及Style的使用 mono版
- monogdb windows环境下 安装及使用简单示例
- 使用Bootstrap + Vue.js实现添加删除数据示例
- 使用js对select动态添加和删除OPTION示例代码