C#文章第四篇之动态编译并生成exe程序,本文主要介绍C#动态编译然后生成exe可执行文件。
接第三篇附件在打上水印之后与源码一起动态编译进exe程序。程序在.NET Framework 4,VistualStudio 2017上测试通过。
动态编译主要利用CSharpCodeProvider类,CSharpCodeProvider提供对 C# 代码生成器和代码编译器的实例的访问权限。通过输入源码文件编译为可执行文件,并将错误返回。
CompilerResults compilerResults = new CSharpCodeProvider().CompileAssemblyFromSource(compilerParameters, new string[]
{
sourceCode
});
编译之前需要将C#代码包含的命名空间、类、方法,以及需要引用的命名空间处理出来,对于引用的控件DLL也需要加载进来一起编译。
第一步、加载需要编译的文件
此部分可以定义一个变量或者放在文件中,项目中为了方便特意将引用部分提取到变量中,而源码部分保存为文件,在打包文件的时候动态加载。
public static string code = @"using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Printing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Data.SqlClient;
using O2S.Components.PDFRender4NET;
";
打包的exe执行的时候部分代码需要admin权限,因此在Main()方法中需要设置相关的权限:
//已管理员权限运行,同时关闭当前程序
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = System.Windows.Forms.Application.ExecutablePath;
startInfo.Verb = "runas";
System.Diagnostics.Process.Start(startInfo);
System.Diagnostics.Process.GetCurrentProcess().Kill();
return;
加载dll
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string resourceName = new AssemblyName(args.Name).Name + ".dll";
using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) { Byte[] assemblyData = new Byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); }
}
第二步、编译源码
通过CompilerParameters设置相关参数:GenerateExecutable设置为true,表示生成可执行文件,OutputAssembly设置输出程序集的名称,CompilerOptions设置编译器编译时的参数,例如运行平台、程序图标等参数,EmbeddedResources是一个集合是打包后的程序包含的资源文件,可以设置程序运行需要的dll文件、相关图像、图标等文件,同时打包的附件(word、excel、pdf)等也包含在EmbeddedResources中。
compilerParameters.EmbeddedResources.Add("O2S.Components.PDFView4NET.dll");
...
compilerParameters.EmbeddedResources.Add("11.jpg");
compilerParameters.EmbeddedResources.Add(Environment.GetEnvironmentVariable("TEMP") + "\\" + current + ".pdf");
在项目中因需要打包的dll较多,为了减少体积,因此将相关dll打包为压缩文件
compilerParameters.EmbeddedResources.Add("dll.zip");
调用CSharpCodeProvider类的CompileAssemblyFromSource方法完成编译
CompilerResults compilerResults = new CSharpCodeProvider().CompileAssemblyFromSource(compilerParameters, new string[]
{
sourceCode
});
CompileAssemblyFromSource方法有两个参数第一个是CompilerParameters用于设置编译器的相关参数和对象,第二个string[]为要编译的源代码字符串的数组。如果编译失败则返回响应的错误通过compilerResults.Errors.HasErrors可以获取到相应内容。
最后,以zip形式打包的dll需要在新程序运行的时候解压出来
string temp = System.Environment.GetEnvironmentVariable("TEMP") + "\\";
//加载程序集中的dll文件,并保存在临时文件夹中
Stream stream = asm.GetManifestResourceStream("FileView.dll.zip");
byte[] srcBuf = new Byte[stream.Length];
stream.Read(srcBuf, 0, srcBuf.Length);
using (FileStream fs = new FileStream(temp + "dll.zip", FileMode.Create, FileAccess.Write))
{
fs.Write(srcBuf, 0, srcBuf.Length);
fs.Close();
}
//zip解压
UnZip(temp + "dll.zip", temp, "");
文件加密项目到此为止,后续整理源码后再发出来