動態創建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");//此處是自身的exe 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"); 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