2013-03-24 65 views
1

我正在C#中編寫一個連接語言,目前它被解釋,但我想要採取下一步:編譯。首先,我嘗試寫一個簡單的「你好,世界!」程序發射器使用System.Reflection.Emit。該代碼工作沒有任何的Emit異常,但是當我運行生成的「TEST.EXE」的文件,它拋出我曾試着用搜索引擎尋找答案異常Reflection.Emit:AssemblyBuilder.SetEntryPoint不設置入口點

Unhandled Exception: System.MissingMethodException: Entry point not found in assembly 'IL_Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. 

,但無濟於事。也許這裏有人可以幫助我? (請)我寫的代碼如下:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Reflection; 
using System.Reflection.Emit; 
using System.IO; 
using System.Diagnostics; 

namespace ILCompileTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      const string ASSEMBLY_NAME = "IL_Test"; 

      AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
       new AssemblyName(ASSEMBLY_NAME), AssemblyBuilderAccess.Save); 
      ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(
       ASSEMBLY_NAME); 
      TypeBuilder typeBuilder = moduleBuilder.DefineType("Program", 
       TypeAttributes.Class | TypeAttributes.Public); 
      MethodBuilder methodBuilder = typeBuilder.DefineMethod(
       "Main", MethodAttributes.Public | MethodAttributes.Static, 
       typeof(void), new Type[] { typeof(string[]) }); 
      ILGenerator gen = methodBuilder.GetILGenerator(); 

      gen.Emit(OpCodes.Ldstr, "Hello, World!"); 
      gen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); 
      gen.Emit(OpCodes.Ldc_I4_1); 
      gen.Emit(OpCodes.Call, typeof(Console).GetMethod("ReadKey", new Type[] { typeof(bool) })); 

      assemblyBuilder.SetEntryPoint(methodBuilder, PEFileKinds.ConsoleApplication); 
      File.Delete("test.exe"); 
      assemblyBuilder.Save("test.exe"); 

      Process.Start("test.exe"); 
     } 
    } 
} 

所以,問題是:我怎麼可以在進入點設置爲Main方法我定義?

回答

1

您正在調用typeBuilder.CreateType(), DefineDynamicModule必須將exe名稱作爲第二個參數傳遞。 Full working sample:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Reflection; 
using System.Reflection.Emit; 
using System.IO; 
using System.Diagnostics; 

namespace ILCompileTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      const string ASSEMBLY_NAME = "IL_Test"; 

      AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
       new AssemblyName(ASSEMBLY_NAME), AssemblyBuilderAccess.Save); 
      ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(
       ASSEMBLY_NAME, "test.exe"); 
      TypeBuilder typeBuilder = moduleBuilder.DefineType("Program", 
       TypeAttributes.Class | TypeAttributes.Public); 
      MethodBuilder methodBuilder = typeBuilder.DefineMethod(
       "Main", MethodAttributes.HideBySig|MethodAttributes.Public | MethodAttributes.Static, 
       typeof(void), new Type[] { typeof(string[]) }); 
      ILGenerator gen = methodBuilder.GetILGenerator(); 

      gen.Emit(OpCodes.Ldstr, "Hello, World!"); 
      gen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); 
      gen.Emit(OpCodes.Ldc_I4_1); 
      gen.Emit(OpCodes.Call, typeof(Console).GetMethod("ReadKey", new Type[] { typeof(bool) })); 
      typeBuilder.CreateType(); 
      assemblyBuilder.SetEntryPoint(methodBuilder, PEFileKinds.ConsoleApplication); 
      File.Delete("test.exe"); 
      assemblyBuilder.Save("test.exe"); 

      Process.Start("test.exe"); 
     } 
    } 
} 
+0

Thanks!這解決了它!我還發現,我需要Opcodes.Pop Console.ReadKey的返回值,並有一個Opcodes.Ret命令,以使整個事情工作。 – feralin 2013-03-24 19:41:28

+0

你知道爲什麼ModuleBuilder想要文件名嗎?它甚至必須與我傳遞給AssemblyBuilder.Save()的值相同嗎? – feralin 2013-03-24 19:42:22

+0

我認爲這與DefineDynamicModule的重載選擇差有關 - 根據MSDN,兩個重載創建瞬態,兩個創建持久模塊。文件名必須相同,否則應用程序也會失敗。在兩個地方指定文件名可以從多個模塊/文件中創建程序集。 – Alexander 2013-03-25 07:00:41