Mono的原因.塞西尔主张方法导入,而我已经这么做了
本文关键字:导入 方法 张方法 Mono | 更新日期: 2023-09-27 18:13:41
下面是我的代码:
private void ModifyMethods()
{
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@"
using System;
namespace ToIL
{
public class Class1
{
public void Write()
{
Console.WriteLine(""Hello"");
}
}
}");
string assemblyName = System.IO.Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location)
};
CSharpCompilation compilation = CSharpCompilation.Create( assemblyName, syntaxTrees: new[] { syntaxTree }, references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
Mono.Cecil.AssemblyDefinition asm = null;
using (var ms = new MemoryStream())
{
var emitResult = compilation.Emit(ms);
if (emitResult.Success)
{
ms.Seek(0, SeekOrigin.Begin);
asm = Mono.Cecil.AssemblyDefinition.ReadAssembly(ms);
}
}
var class1 = asm.MainModule.Assembly.MainModule.Types.FirstOrDefault(T => T.Name == "Class1");
var Method1 = class1.Methods.FirstOrDefault(M => M.Name == "Write");
var ils = Method1.Body.Instructions;
System.Reflection.MethodInfo mWriteLine = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
Mono.Cecil.AssemblyDefinition asmx = Mono.Cecil.AssemblyDefinition.ReadAssembly(@"EditAsm.exe");
var import = asmx.MainModule.Import(mWriteLine);
foreach (var type in asmx.MainModule.Types)
{
if (type.Name == "<Module>") continue;
foreach (var method in type.Methods)
{
var cilWorker = method.Body.GetILProcessor();
foreach (var il in ils) cilWorker.Append(il);
}
}
asmx.Write(@"d:'test.dll"); // Import Exception
}
这段代码的作用是在程序集ToIL
的Class1
中编译Write
方法。然后将方法体的IL(指令)存储在ils
中。最后将指令添加到EditAsm.exe程序集的每个方法中。
如所述,我已经导入了WriteLine,但仍然在asmx.Write(@"d:'test.dll");
Member 'System.Void System.Console::WriteLine(System.String)' is declared in another module and needs to be imported
这是因为您的IL指令属于另一个模块,因此其中的方法引用对于您要添加它们的模块无效。Import实际上只是创建对外部方法(字段等)的引用,但该引用对特定模块有效。所有的IL指令方法调用操作数都有属于代码中名为"assemblyName"的模块的引用。这条线:
var import = asmx.MainModule.Import(mWriteLine);
实际上什么也不做,因为您没有使用返回值。你如何使用它(一般)?这样的:
cilWorker.Append(Instruction.Create(OpCodes.Call, import));
您可以看到,创建方法调用指令需要您要添加指令的特定模块的方法引用。现在,解决问题的可能方法:
foreach (var type in asmx.MainModule.Types) {
if (type.Name == "<Module>") continue;
foreach (var method in type.Methods) {
var cilWorker = method.Body.GetILProcessor();
foreach (var il in ils) {
// grab method reference
var methodRef = il.Operand as Mono.Cecil.MethodReference;
if (methodRef != null) {
// if it belongs to another module
if (methodRef.Module.Name == (assemblyName + ".dll")) {
// resolve it back to method definition and then import,
// now to the correct module. Assign result back to opcode operand
il.Operand = asmx.MainModule.Import(methodRef.Resolve());
}
}
cilWorker.Append(il);
}
}
}
那么你的代码将不再抛出