using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.IO; using System.Linq; using System.Windows.Forms; using System.Xml.Linq; using Newtonsoft.Json; namespace MSBuild { public partial class FormMain : Form { #region 变量 /// /// 配置信息保存路径 /// private static readonly string Dir = AppDomain.CurrentDomain.BaseDirectory + @"SaveDir\"; /// /// 需要编译的文件路径 /// private List _listProject; /// /// 替换dll /// private string[] _listDll; /// /// 获取编译工具路径 /// private string _versionUrl; /// /// 获取编译模式 /// private string _configure; /// /// 指定文件夹 /// private string _saveUrl; /// /// 编译的日志 /// private string _buildLog; /// /// 记录替换过程中发生的信息 /// private string _replaceText; /// /// 替换的线程 /// private readonly BackgroundWorker _replaceWorker = new BackgroundWorker(); /// /// 编译的线程 /// private readonly BackgroundWorker _buildWorker = new BackgroundWorker(); #endregion #region 窗体方法 public FormMain() { InitializeComponent(); } /// /// 响应窗体的Load事件 /// private void FormMain_Load(object sender, EventArgs e) { projectFileText.ReadOnly = dllFileText.ReadOnly = outputText.ReadOnly = buildText.ReadOnly = true; buttonReplace.Enabled = false; // 获取编译工具 BindVersion(); // 获取编译模式 BindModel(); // 设置线程属性 _replaceWorker.WorkerReportsProgress = _buildWorker.WorkerReportsProgress = true; // 进度更新 // 设置线程处理的对象 _replaceWorker.DoWork += _replaceWorker_DoWork; _replaceWorker.ProgressChanged += _replaceWorker_ProgressChanged; _replaceWorker.RunWorkerCompleted += _replaceWorker_RunWorkerCompleted; _buildWorker.DoWork += _buildWorker_DoWork; _buildWorker.ProgressChanged += _buildWorker_ProgressChanged; _buildWorker.RunWorkerCompleted += _buildWorker_RunWorkerCompleted; } #endregion #region 对象方法 /// /// 打开文件文件夹 /// private void buttonOpenProject_Click(object sender, EventArgs e) { var dialog = new FolderBrowserDialog { Description = @"请选择需要编译的文件路径!" }; bool? result = dialog.ShowDialog() == DialogResult.OK; if (result == true) { projectFileText.Text = dialog.SelectedPath; } } /// /// 打开替换Dll文件夹 /// private void buttonOpenDll_Click(object sender, EventArgs e) { var dialog = new FolderBrowserDialog { Description = @"请选择替换Dll的文件路径!" }; bool? result = dialog.ShowDialog() == DialogResult.OK; if (result == true) { dllFileText.Text = dialog.SelectedPath; } } /// /// 查找需要替换的文件 /// private void buttonAnalysis_Click(object sender, EventArgs e) { /* 清空显示 */ projectText.Text = ""; // 判断编译路径是否存在 if (string.IsNullOrWhiteSpace(projectFileText.Text)) { MessageBox.Show(@"编译文件夹路径不能为空!"); return; } // 判断文件夹是否存在 if (!Directory.Exists(projectFileText.Text)) { MessageBox.Show(@"编译文件夹路径不存在"); return; } // 判断替换路径是否存在 if (string.IsNullOrWhiteSpace(dllFileText.Text)) { MessageBox.Show(@"替换路径不能为空!"); return; } // 判断替换文件夹是否存在 if (!Directory.Exists(dllFileText.Text)) { MessageBox.Show(@"替换文件夹不存在!"); return; } // 获取编译路径下的所有.csproj文件 _listProject = Directory.GetFiles(projectFileText.Text, "*.csproj", SearchOption.AllDirectories).ToList(); // 若不存在,则提示 if (_listProject.Count == 0) { MessageBox.Show(@"不存在需要编译的文件"); return; } foreach (var project in _listProject) { projectText.Text += project + Environment.NewLine; } // 获取替换dll _listDll = Directory.GetFiles(dllFileText.Text, "*.dll", SearchOption.AllDirectories); if (_listDll.Length == 0) { MessageBox.Show(@"替换路径下,没有发现可以替换的DLL!"); buttonReplace.Enabled = false; } buttonReplace.Enabled = true; } /// /// 替换引用 /// private void buttonReplace_Click(object sender, EventArgs e) { // 替换路径不能为空 if (string.IsNullOrWhiteSpace(dllFileText.Text)) { MessageBox.Show(@"替换路径不能为空!"); return; } // 判断是否正在异步执行 if (!_replaceWorker.IsBusy) { _replaceWorker.RunWorkerAsync(); } } /// /// 选择输出文件夹 /// private void buttonOutputFile_Click(object sender, EventArgs e) { var dialog = new FolderBrowserDialog { Description = @"请选择输出的文件路径!" }; bool? result = dialog.ShowDialog() == DialogResult.OK; if (result == true) { outputText.Text = dialog.SelectedPath; } } /// /// 编译 /// private void buttonBuild_Click(object sender, EventArgs e) { /* 清空显示 */ buildText.Text = ""; // 判断编译源是否为空 if (_listProject == null || _listProject.Count == 0) { MessageBox.Show(@"请先查找!"); return; } // 判断输出路径是否存在 if (!Directory.Exists(outputText.Text)) { MessageBox.Show(@"指定输出路径不存在"); return; } // 获取编译工具路径 _versionUrl = ((ItemModel)versionCom.SelectedItem).Value; // 获取编译模式 _configure = ((ItemModel)configureBox.SelectedItem).Value; // 指定文件夹 _saveUrl = outputText.Text; // 判断是否正在执行异步操作 if (!_buildWorker.IsBusy && !_replaceWorker.IsBusy) { _buildWorker.RunWorkerAsync(); } } /// /// 保存配置的事件 /// private void SaveConfigure_Click(object sender, EventArgs e) { //获取当前配置 var config = GetConfigModel(); //保存路径不存在时,创建 if (!Directory.Exists(Dir)) { Directory.CreateDirectory(Dir); } //若文件不存在则创建 var filePath = Dir + "config.txt"; var file = new StreamWriter(filePath, false); //写入 file.Write(JsonConvert.SerializeObject(config)); //关闭 file.Close(); file.Dispose(); //提示 MessageBox.Show(@"保存成功"); } /// /// 读取配置的事件 /// private void ReadConfigure_Click(object sender, EventArgs e) { try { // 获取文件内容 if (!File.Exists(Dir + "config.txt")) { return; } var file = new StreamReader(Dir + "config.txt"); var str = file.ReadToEnd(); file.Close(); file.Dispose(); if (string.IsNullOrWhiteSpace(str)) { return; } // 反序列化 var config = JsonConvert.DeserializeObject(str); if (config == null) { return; } // 赋值 projectFileText.Text = config.Url; dllFileText.Text = config.PathUrl; outputText.Text = config.OutputUrl; versionCom.SelectedValue = config.VersionUlr; configureBox.SelectedValue = config.ModelKey; } catch (Exception exception) { MessageBox.Show(exception.Message); } } /// /// 响应_replaceWorker的DoWork事件 /// private void _replaceWorker_DoWork(object sender, DoWorkEventArgs e) { // 清空文本 _replaceText = ""; // 获取替换的dll var dllFiles = _listDll .Select(file => new DirectoryInfo(file)).Select(path => new AnalysisModel { NewName = path.Name, NewPath = path.FullName }) .ToList(); // 循环获取替换 var log = ""; var count = _listProject.Count; for (var index = count - 1; index >= 0; index--) { _replaceWorker.ReportProgress(int.Parse(Math.Ceiling((count - index) / count * 100.0).ToString(CultureInfo.CurrentCulture))); var project = _listProject[index]; // 获取项目名称 var projectName = project.Substring(project.LastIndexOf('\\') + 1); var name = projectName.Substring(0, projectName.LastIndexOf('.')); //获取.csproj文件中的所有非系统引用 var doc = XDocument.Load(project); var element = (doc.Root.Elements()) .First(x => x.Name.LocalName == "ItemGroup"); if (element == null) { log += name + ":项目中没有引用第三方不需替换。" + Environment.NewLine; _replaceText += name + "替换失败" + Environment.NewLine; continue; } var reference = element.Elements() .Where(x => x.Name.LocalName == "Reference") .ToList(); var projectLists = reference .Select(el => new AnalysisModel() { OldName = el.Attribute("Include").Value, OldPath = el.Value }) .Where(x => !string.IsNullOrWhiteSpace(x.OldPath)) .ToList(); if (projectLists.Count == 0) { log += name + ":项目中没有引用第三方不需替换。" + Environment.NewLine; _replaceText += name + "替换失败" + Environment.NewLine; continue; } //根据获取的引用查找替换文件夹中的引用 var list = (from projectList in projectLists let sp = projectList.OldName.Split(',') let y = sp.Length == 0 ? projectList.OldName : sp[0] let split = y.Split(new[] { ".v" }, StringSplitOptions.None) let x = split.Length == 1 ? y : split[1] == "1" ? y : split[0] let z = split.Length < 2 || split[1].IndexOf('.') + 1 == split[1].Length - 1 ? "" : split[1].Substring(split[1].IndexOf('.', split[1].IndexOf('.') + 1)) let contain = dllFiles.Where(a => a.NewName.Contains(x)).ToList() let contains = z == "" ? contain : contain.Where(b => b.NewName.Contains(z)).ToList() where contains.Count != 0 select new AnalysisModel { OldName = projectList.OldName, OldPath = projectList.OldPath, NewName = contains[0].NewName, NewPath = contains[0].NewPath }) .ToList(); var listExcept = (from projectList in projectLists let sp = projectList.OldName.Split(',') let y = sp.Length == 0 ? projectList.OldName : sp[0] let split = y.Split(new[] { ".v" }, StringSplitOptions.None) let x = split.Length == 0 ? projectList.OldName : split[0] let contain = dllFiles.Where(z => z.NewName.Contains(x)).ToList() where contain.Count == 0 select new AnalysisModel { OldName = y }) .ToList(); if (list.Count == 0) { log += name + ":所引用的第三方dll,在引用文件夹中全部不存在" + Environment.NewLine; _replaceText += name + "替换失败" + Environment.NewLine; _listProject.Remove(project); //不进行编译 continue; } if (listExcept.Count != 0) { var listDll = string.Join(",", listExcept.Select(x => x.OldName)); log += name + ":所引用的第三方dll" + listDll + ",在引用文件夹中不存在" + Environment.NewLine; _replaceText += name + "替换失败" + Environment.NewLine; _listProject.Remove(project); //不进行编译 continue; } //获取.csproj文件中的所有非系统引用 var elements = element.Elements().Where(x => !string.IsNullOrWhiteSpace(x.Value)).ToList(); for (var i = elements.Count - 1; i >= 0; i--) { var elem = elements[i]; var old = list.Where(x => x.OldName == elem.Attribute("Include").Value).ToList(); if (old.Count == 0) continue; //添加新节点 XNamespace nameNamespace = element.Name.NamespaceName; var referenceName = new XElement(nameNamespace + "Reference"); var newName = old[0].NewName.Split(new[] { ".dll" }, StringSplitOptions.None); referenceName.SetAttributeValue("Include", newName.Length == 0 ? old[0].NewName : newName[0]); referenceName.SetElementValue(nameNamespace + "HintPath", old[0].NewPath); element.AddFirst(referenceName); //移除当前节点 elem.Remove(); } doc.Save(project, SaveOptions.OmitDuplicateNamespaces); _replaceText += name + "替换成功" + Environment.NewLine; } //保存路径不存在时,创建 if (!Directory.Exists(Dir)) { Directory.CreateDirectory(Dir); } //若文件不存在则创建 var filePath = Dir + "log.txt"; var logFile = new StreamWriter(filePath, false); //写入 logFile.Write(log); //关闭 logFile.Close(); logFile.Dispose(); //提示 _replaceText += "如有替换失败,请查看log.txt日志"; } /// /// 响应_replaceWorker的ProgressChanged事件 /// private void _replaceWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { replaceProgress.Value = e.ProgressPercentage; } /// /// 响应_replaceWorker的RunWorkerCompleted事件 /// private void _replaceWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { MessageBox.Show(e.Cancelled ? @"进程已被取消:" + replaceProgress.Value + "%" : @"进程执行完成:" + replaceProgress.Value + "%"); replaceProgress.Value = 0; projectText.Text = _replaceText; } /// /// 响应_buildWorker的DoWork事件 /// private void _buildWorker_DoWork(object sender, DoWorkEventArgs e) { //循环编译 string errLog = ""; var count = _listProject.Count; for (var index = 0; index < _listProject.Count; index++) { var project = _listProject[index]; // 获取编译文件路径 var buildUrl = project; // 获取输出路径,不存在则新建,存在则删除文件夹中的文件 var outputUrl = buildUrl.Substring(0, buildUrl.LastIndexOf('\\')) + "\\bin\\" + _configure; if (!Directory.Exists(outputUrl)) { Directory.CreateDirectory(outputUrl); } else { DeleteDir(outputUrl); } // 定义编译脚本 var cmd = ""; cmd += "@echo off"; cmd += "\r\n"; cmd += "\"" + _versionUrl + "\" " + "\"" + buildUrl + "\" " + "/t:rebuild /p:Configuration=" + _configure + " /p:OutDir=" + "\"" + outputUrl + "\""; // 编译 string output; ComHelper.RunCmd(cmd, out output); _buildLog = output + Environment.NewLine; // 复制文件到指定文件夹 var file = buildUrl.Substring(buildUrl.LastIndexOf('\\')); var fileName = file.Substring(0, file.LastIndexOf('.')) + ".dll"; if (!File.Exists(outputUrl + '\\' + fileName)) { fileName = file.Substring(0, file.LastIndexOf('.')) + ".exe"; } try { File.Copy(outputUrl + '\\' + fileName, _saveUrl + '\\' + fileName, true); } catch { errLog += output; } _buildWorker.ReportProgress(int.Parse(Math.Ceiling((index + 1.0) / count * 100).ToString(CultureInfo.CurrentCulture))); } //保存路径不存在时,创建 if (!Directory.Exists(Dir)) { Directory.CreateDirectory(Dir); } //若文件不存在则创建 var filePath = Dir + "errors.txt"; var logFile = new StreamWriter(filePath, false); //写入 logFile.Write(errLog); //关闭 logFile.Close(); logFile.Dispose(); } /// /// 响应_buildWorker的ProgressChanged事件 /// private void _buildWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { buildProgress.Value = e.ProgressPercentage; buildText.Text += _buildLog; } /// /// 响应_buildWorker的RunWorkerCompleted事件 /// private void _buildWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { MessageBox.Show(e.Cancelled ? @"进程已被取消:" + buildProgress.Value + "%" : @"进程执行完成:" + buildProgress.Value + "%"); buildProgress.Value = 0; } #endregion #region 内部方法 /// /// 获取并绑定编译工具 /// private void BindVersion() { //获取MSBuild var exeName = "MSBuild.exe"; var msBuilds = Directory.GetFiles(@"C:\Program Files (x86)", exeName, SearchOption.AllDirectories); if (msBuilds.Length == 0) { MessageBox.Show(@"请先安装vs"); return; } var listVersion = (from msBuild in msBuilds let split = msBuild.Split(new[] { "\\", "/" }, StringSplitOptions.RemoveEmptyEntries) where split.Length > 3 let amd = split[split.Length - 2] let version = amd.Contains("64") ? "(x64)" : "(x86)" select new ItemModel(split[3] + version, msBuild)) .ToList(); versionCom.DisplayMember = "Key"; versionCom.ValueMember = "Value"; versionCom.DataSource = listVersion; } /// /// 绑定编译模式 /// private void BindModel() { var dataSource = new List { new ItemModel("1", "Debug"), new ItemModel("2", "Release") }; //编译模式 configureBox.DisplayMember = "Value"; configureBox.ValueMember = "Key"; configureBox.DataSource = dataSource; } /// /// 获取当前配置 /// private ConfigureModel GetConfigModel() { var config = new ConfigureModel() { Url = projectFileText.Text, PathUrl = dllFileText.Text, OutputUrl = outputText.Text }; if (versionCom.SelectedItem is ItemModel) { ItemModel versionItem = versionCom.SelectedItem as ItemModel; config.Version = versionItem.Key; config.VersionUlr = versionItem.Value; } if (configureBox.SelectedItem is ItemModel) { ItemModel modelItem = configureBox.SelectedItem as ItemModel; config.Model = modelItem.Value; config.ModelKey = modelItem.Key; } return config; } /// /// 删除文件夹下的所有文件 /// private void DeleteDir(string srcPath) { try { DirectoryInfo dir = new DirectoryInfo(srcPath); FileSystemInfo[] fileInfo = dir.GetFileSystemInfos(); foreach (FileSystemInfo file in fileInfo) { if (file is DirectoryInfo) { DirectoryInfo subDir = new DirectoryInfo(file.FullName); subDir.Delete(true); } else { File.Delete(file.FullName); } } } catch (Exception) { throw; } } #endregion } }