2017-10-13 65 views
1
open System 
open Mono.Cecil 
open Mono.Cecil.Cil 

let myHelloWorldApp = 
    AssemblyDefinition.CreateAssembly(
     new AssemblyNameDefinition("HelloWorld", new Version(1, 0, 0, 0)), "HelloWorld", ModuleKind.Console) 

let module_ = myHelloWorldApp.MainModule 

// create the program type and add it to the module 
let programType = 
    new TypeDefinition("HelloWorld", "Program", 
     Mono.Cecil.TypeAttributes.Class ||| Mono.Cecil.TypeAttributes.Public, module_.TypeSystem.Object) 

module_.Types.Add(programType) 

// add an empty constructor 
let ctor = 
    new MethodDefinition(".ctor", Mono.Cecil.MethodAttributes.Public ||| Mono.Cecil.MethodAttributes.HideBySig 
     ||| Mono.Cecil.MethodAttributes.SpecialName ||| Mono.Cecil.MethodAttributes.RTSpecialName, module_.TypeSystem.Void) 

// create the constructor's method body 
let il = ctor.Body.GetILProcessor() 

il.Append(il.Create(OpCodes.Ldarg_0)) 

// call the base constructor 
il.Append(il.Create(OpCodes.Call, module_.ImportReference(typeof<obj>.GetConstructor([||])))) 

il.Append(il.Create(OpCodes.Nop)) 
il.Append(il.Create(OpCodes.Ret)) 

programType.Methods.Add(ctor) 

// define the 'Main' method and add it to 'Program' 
let mainMethod = 
    new MethodDefinition("Main", 
     Mono.Cecil.MethodAttributes.Public ||| Mono.Cecil.MethodAttributes.Static, module_.TypeSystem.Void) 

programType.Methods.Add(mainMethod) 

// add the 'args' parameter 
let argsParameter = 
    new ParameterDefinition("args", 
     Mono.Cecil.ParameterAttributes.None, module_.ImportReference(typeof<string[]>)) 

mainMethod.Parameters.Add(argsParameter); 

// create the method body 
il = mainMethod.Body.GetILProcessor() 

il.Append(il.Create(OpCodes.Nop)) 
il.Append(il.Create(OpCodes.Ldstr, "Hello World")) 

let writeLineMethod = 
    il.Create(OpCodes.Call, 
     module_.ImportReference(typeof<Console>.GetMethod("WriteLine", [|typeof<string>|]))) 

// call the method 
il.Append(writeLineMethod) 

il.Append(il.Create(OpCodes.Nop)) 
il.Append(il.Create(OpCodes.Ret)) 

// set the entry point and save the module 
myHelloWorldApp.EntryPoint <- mainMethod 

我向这个问题的答案借了the example,并用F#重写了它。当我尝试运行它,我得到以下错误:为什么Mono.Cecil HelloWorld示例失败并出现异常?

Unhandled Exception: System.TypeLoadException: Could not load type 'HelloWorld.Program' from assembly 'Hello 
on=1.0.0.0, Culture=neutral, PublicKeyToken=null' because the method 'Main' has no implementation (no RVA). 

这里有什么问题?

回答

2

我的猜测是,该问题由以下行引起的:

// create the method body 
il = mainMethod.Body.GetILProcessor() 

在C#,这将分配为Main方法将il变量的IL处理器,但在F#,这仅是一个相等测试结果为false - 因此您想要为Main方法生成的IL代码被添加到之前构造函数的il处理器中。

你应该能够解决这个问题使用可变阴影:

// create the method body 
let il = mainMethod.Body.GetILProcessor() 
+0

是啊,这是它。尽管不是使用let语句,但最好将它绑定到lambda,否则会发生重复的变量错误。另外,我之前已经注意到,由于某种原因,F#没有给我通常的平等测试没有单位警告,但是我没有再次检查相同的错误。谢谢。 –

+1

@MarkoGrdinic您不会在脚本的顶层获得平等测试警告(您可能需要编写大量表达式并逐个运行它们)。如果你的代码是在一个函数内部的话,你会得到那些(反正这是一个更好的结构) –

相关问题