通用技术 使用 C# 动态生成程序集并保存为可执行的 HelloWorld.exe

SinDynasty · June 14, 2018 · 1139 hits

首先我们要先定义一个 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");
No Reply at the moment.
需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up