2017-10-13 191 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您不會在腳本的頂層獲得平等測試警告(您可能需要編寫大量表達式並逐個運行它們)。如果你的代碼是在一個函數內部的話,你會得到那些(反正這是一個更好的結構) –

相關問題