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
}
}