首先我们要先定义一个 HelloWorld 程序集的名称。
AssemblyName HelloWorldAssemblyName = new AssemblyName("HelloWorldAssembly");
然后我们根据该 AssemblyName,调用默认的应用程序域 AppDomain.CurrentDomain 的 DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access) 方法,生成一个 AssemblyBuilder。
备注:其中 AssemblyBuilderAccess.RunAndSave 表示用该 AssemblyBuilder 生成的程序集是可以运行和保存的;另外该 DefineDynamicAssembly 方法只能通过默认的 AppDomain 调用,即只能通过 AppDomain.CurrentDomain 调用。
AssemblyBuilder HelloWorldAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(HelloWorldAssemblyName, AssemblyBuilderAccess.RunAndSave);
在我们创建好 AssemblyBuilder 后,我们需要调用其 DefineDynamicModule(string name, string fileName) 方法,再创建一个相应的 ModuleBuilder。
备注:其中要注意的是该 DefineDynamicModule 方法中第 2 个参数虽然表示的是文件名,但其不能是一个路径,生成该 HelloWorld.exe 所在的目录是在 AppDomain.CurrentDomain.BaseDirectory 下。
ModuleBuilder HelloWorldModuleBuilder = HelloWorldAssemblyBuilder.DefineDynamicModule("HelloWorldModule", "HelloWorld.exe");
在我们创建好 ModuleBuilder 后,我们需要调用其 DefineType(string name, TypeAttributes attr) 方法,来创建一个相应的 TypeBuilder。
备注:TypeAttributes.Public | TypeAttributes.Class 表示要生成的类型是一个公共的类。
TypeBuilder HelloWorldTypeBuild = HelloWorldModuleBuilder.DefineType("HelloWorldNamespace.HelloWorldClass", TypeAttributes.Public | TypeAttributes.Class);
在我们创建好 TypeBuilder 后,我们需要调用其 DefineMethod(string name, MethodAttributes attributes) 方法,来创建一个相应的 MethodBuilder。
备注:MethodAttributes.Public | MethodAttributes.Static 表示要生成的方法是一个公共的静态方法。
MethodBuilder HelloWorldMethodBuilder = HelloWorldTypeBuild.DefineMethod("MainMethod", MethodAttributes.Public | MethodAttributes.Static);
在我们创建好 MethodBuilder 后,我们需要调用其 GetILGenerator() 方法,来获得一个中间语言生成器 ILGenerator。
备注:大多数的情况下.NET 代码都会翻译成中间语言 IL,然后又会在 CLR 下被转化为机器语言。
ILGenerator HelloWorldILGenerator = HelloWorldMethodBuilder.GetILGenerator();
调用 ILGenerator 的 Emit(OpCode opcode, string str) 方法,构建出 “HelloWorld” 字符串。
备注:OpCode 表示的是中间语言的指令,在这儿需要使用的是 OpCodes.Ldstr。
HelloWorldILGenerator.Emit(OpCodes.Ldstr, "HelloWorld");
调用 ILGenerator 的 Emit(OpCode opcode, MethodInfo meth) 方法,指示程序下一步是调用 Console.WriteLine(string value) 方法和 Console.ReadKey() 方法。
备注:OpCode 表示的是中间语言的指令,在这儿需要使用的是 OpCodes.Call。
HelloWorldILGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[1] { typeof(string) }));
HelloWorldILGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("ReadKey", Type.EmptyTypes));
调用 ILGenerator 的 Emit(OpCode opcode) 方法,指示程序移除当前堆栈顶部的值。
备注:OpCode 表示的是中间语言的指令,在这儿需要使用的是 OpCodes.Pop
HelloWorldILGenerator.Emit(OpCodes.Pop);
调用 ILGenerator 的 Emit(OpCode opcode) 方法,指示程序从当前所执行的方法中返回值,当然这里是没有值返回的,其将起到的作用就相当于从当前方法中退出。
备注:OpCode 表示的是中间语言的指令,在这儿需要使用的是 OpCodes.Ret。
现在我们就大致上构建好了这个 HelloWorld 的代码,接着我们需要做的是调用 TypeBuilder 中的 CreateType() 方法,在内存中真正生成所需的程序集及其类型。
Type HelloWorldType = HelloWorldTypeBuild.CreateType();
接着我们调用 AssemblyBuilder 中的 SetEntryPoint(MethodInfo entryMethod, PEFileKinds fileKind) 方法,设置该 HelloWorld.exe 程序的入口点。
备注:PEFileKinds.ConsoleApplication 表示生成的是一个控制台程序。
HelloWorldAssemblyBuilder.SetEntryPoint(HelloWorldType.GetMethod("MainMethod"), PEFileKinds.ConsoleApplication);
调用 AssemblyBuilder 中的 Save(string assemblyFileName) 方法,将其保存。
备注:其中要注意的是该 Save 方法中的参数虽然表示的是文件名,但其不能是一个路径,生成该 HelloWorld.exe 所在的目录是在 AppDomain.CurrentDomain.BaseDirectory 下
HelloWorldAssemblyBuilder.Save("HelloWorld.exe");