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

sindynasty · 2018年06月14日 · 279 次阅读

首先我们要先定义一个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");
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册