文章原文来自:https://www.mono-project.com/docs/tools+libraries/libraries/Mono.Cecil/faq/
翻译:Jeffrey Chou
Mono.Cecil FAQ文档翻译
目录
目录
我要增加一些跟踪功能到一个不能调试程序集,我使用Cecil可以做到吗?
现在我已经下载这个类库了,那么我应该如何使用它?
这里是一个显示一个托管的程序集中所有的含有的类型的程序代码:
//Creates an AssemblyDefinition from the "MyLibrary.dll" assembly
AssemblyDefinition myLibrary = AssemblyDefinition.ReadAssembly ("MyLibrary.dll");
//Gets all types which are declared in the Main Module of "MyLibrary.dll"
foreach (TypeDefinition type in myLibrary.MainModule.Types) {
//Writes the full name of a type
Console.WriteLine (type.FullName);
}
我们可以看到控制台中的输出结果:
<Module>
MyLibrary.Company
MyLibrary.Employee
MyLibrary.Person
上面的代码中创建了一个对应于MyLibrary程序集的Mono.Cecil.AssemblyDefinition对象。你也可以获得一个程序集中包含的一个ModuleDefinitions对象(通过使用AssemblyDefinition.Modules的属性)。一般说来,你需要通过使用一个名称为MaiModule(通过使用AssemblyDefinition.MainModule的属性)来获得模块。
Cecil中提供对象模型的程序集中包含的实体是什么?
下面是Cecil用来做操作处理主要实体和他们之间的关系的一幅简化类图:
通过载入一个程序集,可以使用静态方法ReadAssembly来创建一个AssemblyDefinition类型对象。每一个的AssemblyDefinition类型的对象都包含了一个ModuleDefinitions集合。一般来说,我们会使用主模块的ModuleDefinitiond(你可以通过MainModule 的属性进行获得它)来进行操作。
在一个ModuleDefinition类型的对象中包含了TypeDefinition的对象。任意个TypeDefinition类型对象中含有以下类型的集合:
●MethodDefinition
●FieldDefinition
●PropertyDefinition
通过使用构造函数属性,你们也可以获得一个类型的构造函数。一个构造器是一个MethodDefinition类型的对象。一个MethodDefinition属性的定义拥有两个MethodDefinitions,它们分别对应的是Get方法和Set方法。
一个MethodDefinition类型对象包含了一个方法体的类型对象MethodBody。通过CilWorker对象属性,你可以获得一个方法定义MethodDefinition类型对象的所有的CIL指令。此外,MethodDefinition类型对象包含一个指令集合的属性(Instructions property)。
我要增加一些跟踪功能到一个不能调试程序集,我使用Cecil可以做到吗?
是的可以做到。这种技术叫做AOP。这里有一些简单的列子关于如何去做。如果你想制造一些更先进的东西,你必须学习CIL。
我们采用和先前一样的程序集的名称MyLibrary作为例子。代替打印每一个类型的名称,现在的程序作用是将下面的代码插入到程序集的每一个类型的每一个方法中去:
插入的代码为:
Console.WriteLine ("Code added in ", method.Name);
第一件要做的事实获得对应于Console.WriteLine (string value)方法的System.Reflection.MethodInfo对象。
//Gets the MethodInfo of Console.WriteLine() method
MethodInfo writeLineMethod =typeof(Console).GetMethod("WriteLine", new Type[]{typeof(string)});
然后,为了去插入MSIL指令,你需要获得所有的程序集中每一个类型的每一个方法。
//Getting the path of the "MyLibrary.dll" assembly
string pathBin = "../../../MyLibrary/bin/debug/MyLibrary.dll";
//Gets the AssemblyDefinition of "MyLibrary"
AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(pathBin);
//Gets all types of the MainModule of the assembly
foreach(TypeDefinition type in assembly.MainModule.Types)
{
if(type.Name != "<Module>")
{
//Gets all methods of the current type
foreach(MethodDefinition method in type.Methods)
{
//Gets the CilWorker of the method for working with CIL instructions
CilWorker worker = method.Body.CilWorker;
(译者注:CilWorker类被移除,并且它被重命名为了ILProcessor。你可以通过在一个MethodBody对象上调用GetILProcessor来获得一个ILProcessor的对象。所以上面的
CilWorker worker = method.Body.CilWorker;
应该为:
ILProcessor ilProcessor= method.Body. GetILProcessor();)
//Creating a sentence according to the current method
string sentence;
sentence = String.Concat("Code added in ", method.Name);
//Import the Console.WriteLine() method
MethodReference writeLine;
writeLine = assembly.MainModule.Import(writeLineMethod);
//Creates the MSIL instruction for inserting the sentence
Instruction insertSentence;
insertSentence = worker.Create(OpCodes.Ldstr, sentence);
//Creates the CIL instruction for calling the
//Console.WriteLine(string value) method
Instruction callWriteLine;
callWriteLine = worker.Create(OpCodes.Call, writeLine);
//Getting the first instruction of the current method
Instruction ins = method.Body.Instructions[0];
//Inserts the insertSentence instruction before the first //instruction
method.Body.CilWorker.InsertBefore(ins, insertSentence);
//Inserts the callWriteLineMethod after the //insertSentence instruction
worker.InsertAfter(insertSentence, callWriteLine);
}
}
}
最后要做的事是将包含了修改了的类型的程序集进行保存:
//Save the modified "MyLibrary" assembly
assembly.Write(pathBin);
在执行了上面的代码之后,你可以在你的新的控制台工程中使用上面修改过的程序集。与此同时你需要去引用这个修改过的程序集。
MyLibrary.Person p = new MyLibrary.Person();
p.Name = "Harry May";
p.Birthday = new DateTime(1982, 01, 26);
int age = p.GetAge();
Console.ReadLine();
上述代码产生的执行结果为:
set_Name was called
set_Birthday was called
GetAge was called