原文链接
动态创建EXE功能目的:
比如我们写了设计软件,可以界面设计一些东西。现在我们需要将我们设计好的东西保存成一个exe,用户打开就直接可以看到设计好的东西。也就是需要把数据保存到exe里面。
这个时候呢,我们可以动态生成一个新的程序,这个程序调用我们原先的设计软件,区别是,我们运行这个软件的时候,可以把原先设计好的数据全部写死写入到新的软件里面,
然后参数传入到设计软件。这样就可以实现双击运行软件,打开就是原先设计好的数据了。
废话讲完了,也不知道有没有讲清楚、、、、
1.创建项目SaveExe或者修改代码中SaveExe名字为自己的项目
2.添加按钮调用CreateCodeEXE,即可实现编译生成一个新的exe即 复制了自身的exe生成一个新的exe(目的就是新生的exe,我们可以增加预设参数属性之类)。
public static void CreateCodeEXE(){CSharpCodeProvider provider = new CSharpCodeProvider();CompilerParameters parameters = new CompilerParameters();parameters.GenerateExecutable = true;parameters.CompilerOptions = "/target:winexe /optimize /win32icon:ImageView.ico";parameters.GenerateInMemory = false;string path = "test.exe";parameters.OutputAssembly = path;parameters.ReferencedAssemblies.Add("SaveExe.exe");//此处是自身的exeparameters.ReferencedAssemblies.Add("System.dll");parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");parameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll");parameters.ReferencedAssemblies.Add("System.Core.dll");parameters.ReferencedAssemblies.Add("System.Data.dll");parameters.ReferencedAssemblies.Add("System.Data.DataSetExtensions.dll");parameters.ReferencedAssemblies.Add("System.Deployment.dll");parameters.ReferencedAssemblies.Add("System.Drawing.dll");parameters.ReferencedAssemblies.Add("System.Net.Http.dll");parameters.ReferencedAssemblies.Add("System.Xml.dll");parameters.ReferencedAssemblies.Add("System.Xml.Linq.dll");string sourceFile = @"using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;namespace SaveExe{static class Program{/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new Form1());}}}";CompilerResults cr = provider.CompileAssemblyFromSource(parameters, sourceFile);if (cr.Errors.Count > 0){StringBuilder sb = new StringBuilder();foreach (var er in cr.Errors)sb.AppendLine(er.ToString());MessageBox.Show(sb.ToString());}else{MessageBox.Show("编译成功");}}
以上方法有一个限制就是必须在自身的exe所在的路径下有效,即生成的exe和原本的exe要在同一个目录下。
解决方案,就是嵌入资源,将原本的exe嵌入到新生成的exe。然后使用动态加载。
例子:
程序GraphicPlay.EXE,里面有个Form1,实现生成新的程序,直接弹出Form1
public class SaveEXE{public static void CreateCode(string filename, string source){CSharpCodeProvider provider = new CSharpCodeProvider();CompilerParameters parameters = new CompilerParameters();parameters.GenerateExecutable = true;parameters.CompilerOptions = "/target:winexe /optimize /win32icon:Image.ico";//这里增加了图标,没有对应图标的可以删除parameters.GenerateInMemory = false;string path = filename;parameters.OutputAssembly = path;parameters.ReferencedAssemblies.Add("System.dll");parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");parameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll");parameters.ReferencedAssemblies.Add("System.Core.dll");parameters.ReferencedAssemblies.Add("System.Data.dll");parameters.ReferencedAssemblies.Add("System.Data.DataSetExtensions.dll");parameters.ReferencedAssemblies.Add("System.Deployment.dll");parameters.ReferencedAssemblies.Add("System.Drawing.dll");parameters.ReferencedAssemblies.Add("System.Net.Http.dll");parameters.ReferencedAssemblies.Add("System.Xml.dll");parameters.ReferencedAssemblies.Add("System.Xml.Linq.dll");parameters.EmbeddedResources.Add(Application.ExecutablePath);//这里添加嵌入资源CompilerResults cr = provider.CompileAssemblyFromSource(parameters, source);if (cr.Errors.Count > 0){StringBuilder sb = new StringBuilder();foreach (var er in cr.Errors)sb.AppendLine(er.ToString());throw new Exception(sb.ToString());}}}
SaveEXE.CreateCode(save.FileName,(@"using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;using System.Reflection;using System.IO;namespace GraphicPlay{static class Program{/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);try{Assembly assembly = Assembly.GetExecutingAssembly();Stream stream = assembly.GetManifestResourceStream(""GraphicPlay.EXE"");if(stream==null)throw new Exception(""加载资源失败"");byte[] buffer = new byte[(int)stream.Length];stream.Read(buffer, 0, buffer.Length);stream.Close();Assembly asm = Assembly.Load(buffer);if(stream==null)throw new Exception(""加载程序集失败"");Type t = asm.GetType(""GraphicPlay.Form1"");var form = asm.CreateInstance(t.FullName) as Form;Application.Run(form);}catch(Exception ex){MessageBox.Show(ex.ToString());}}}}"));
有时需要动态引用DLL,可以参考以下方法:(需要先将DLL添加到资源文件中,然后加载资源文件)
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args){string dllName = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll", "");dllName = dllName.Replace(".", "_");if (dllName.EndsWith("_resources")) return null;System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());byte[] bytes = (byte[])rm.GetObject(dllName);return System.Reflection.Assembly.Load(bytes);}public Form1()//看清楚这是窗体本来的初始化函数{//在InitializeComponent()之前调用AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);InitializeComponent();}
参考文章: https://www.iteye.com/problems/72159
https://blog.csdn.net/lin381825673/article/details/39122257?tdsourcetag=s_pcqq_aiomsg
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/target-compiler-option
