声明:核心功能的实现是由园子里圣殿骑士大哥写的,本人是基于他核心代码,按照自己需求进行修改的。
而AutoUpdaterService.xml文件生成工具是基于评论#215楼 ptangbao的代码而改写的。
由于这个组件是在10年写的,.net也有更新有的方法已提示过时,更改如下:
//Added the function to support proxy //clientDownload.Proxy = System.Net.WebProxy.GetDefaultProxy(); clientDownload.Proxy = WebRequest.GetSystemWebProxy();
更改的主要功能如下:
1》如果有更新将会直接更新,不再提供由用户点击确定后再更新。(强制更新)(这个暂时没有整理出来,后续会整理出来)
2》更新前判断主程序进程是否开启:
如果有更新,主程序开启,关闭主程序,更新完成后自动启动主程序。
如果没有更新,直接启动主程序。
3》不再根据版本号不同进行更新。
圣殿骑士大哥的是根据版本号,当然这也是最正规的,可是我们的程序有点特殊,所以不再根据版本号控制,而是根据GUID。
这样就是有一点好处不管版本号一不一样,只要GUID不一样就是要更新。
比如文件夹如下:
使用CreateXmlTools.exe工具生成xml文件,增加的节点属性有version,值时GUID
<?xml version="1.0" encoding="utf-8"?> <updateFiles> <file path="AutoUpdater.dll" url="http://172.30.100.55:8011/AutoUpdater.dll" lastver="5.0.0.0" size="26624" needRestart="false" version="1ef2b9dc-d14f-4fc4-a5ec-bdb07a6ba98c" /> <file path="ReadMe.dll" url="http://172.30.100.55:8011/ReadMe.dll" lastver="" size="472" needRestart="false" version="3ddc1926-3088-468f-9088-92b07156c757" /> <file path="aspnet_client/ReadMe.dll" url="http://172.30.100.55:8011/aspnet_client/ReadMe.dll" lastver="" size="472" needRestart="false" version="4aaa87e2-63bd-486a-9957-1c2df21607cb" /> </updateFiles>
version就是用来替代lastver的,只要不一样就更新
4》客户端更新主程序更改autoupdater.config文件的更新方式
config里不必包含所有文件的配置,只要求配置成如下:
<?xml version="1.0" encoding="utf-8" ?> <Config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Enabled>true</Enabled> <ServerUrl>http://172.30.100.55:8011/AutoupdateService.xml</ServerUrl> <UpdateFileList> </UpdateFileList> </Config>
ServerUrl就是web服务器的地址加上面生成xml文件的地址。
更新完成后客户端会自动更新autoupdater.config文件,将本地的guid保持与服务端一致,再次点击guid一致的不再更新
以上是大致改动的地方。
下面来说说代码吧:
由于增加了GUID这块,所以RemoteFile、LocalFile和DownloadFileInfo三个实体类都应该增加一个字段和一个属性
LocalFile.cs
class="code_img_closed" src="/Upload/Images/2013112815/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('95e16e3b-2c48-43fc-bfff-588e27a768db',event)" src="/Upload/Images/2013112815/2B1B950FA3DF188F.gif" alt="" />1 public class LocalFile 2 { 3 #region The private fields 4 private string path = ""; 5 private string lastver = ""; 6 private int size = 0; 7 private string version = ""; 8 #endregion 9 10 #region The public property 11 [XmlAttribute("path")] 12 public string Path { get { return path; } set { path = value; } } 13 [XmlAttribute("lastver")] 14 public string LastVer { get { return lastver; } set { lastver = value; } } 15 [XmlAttribute("size")] 16 public int Size { get { return size; } set { size = value; } } 17 [XmlAttribute("version")] 18 public string Version { get { return version; } set { version = value; } } 19 #endregion 20 21 #region The constructor of LocalFile 22 public LocalFile(string path, string ver, int size,string versionid) 23 { 24 this.path = path; 25 this.lastver = ver; 26 this.size = size; 27 this.version = versionid; 28 } 29 30 public LocalFile() 31 { 32 } 33 #endregion 34 35 }View Code
RemoteFile.cs
1 public class RemoteFile 2 { 3 #region The private fields 4 private string path = ""; 5 private string url = ""; 6 private string lastver = ""; 7 private int size = 0; 8 private bool needRestart = false; 9 private string version = ""; 10 #endregion 11 12 #region The public property 13 public string Path { get { return path; } } 14 public string Url { get { return url; } } 15 public string LastVer { get { return lastver; } } 16 public int Size { get { return size; } } 17 public bool NeedRestart { get { return needRestart; } } 18 public string Verison { get { return version; } } 19 #endregion 20 21 #region The constructor of AutoUpdater 22 public RemoteFile(XmlNode node) 23 { 24 this.path = node.Attributes["path"].Value; 25 this.url = node.Attributes["url"].Value; 26 this.lastver = node.Attributes["lastver"].Value; 27 this.size = Convert.ToInt32(node.Attributes["size"].Value); 28 this.needRestart = Convert.ToBoolean(node.Attributes["needRestart"].Value); 29 this.version = node.Attributes["version"].Value; 30 } 31 #endregion 32 }View Code
DownloadFileInfo.cs
1 public class DownloadFileInfo 2 { 3 #region The private fields 4 string downloadUrl = string.Empty; 5 string fileName = string.Empty; 6 string lastver = string.Empty; 7 int size = 0; 8 string version = string.Empty; 9 #endregion 10 11 #region The public property 12 public string DownloadUrl { get { return downloadUrl; } } 13 public string FileFullName { get { return fileName; } } 14 public string FileName { get { return Path.GetFileName(FileFullName); } } 15 public string LastVer { get { return lastver; } set { lastver = value; } } 16 public int Size { get { return size; } } 17 public string Version { get { return version; } set { version = value; } } 18 #endregion 19 20 #region The constructor of DownloadFileInfo 21 public DownloadFileInfo(string url, string name, string ver, int size,string versionid) 22 { 23 this.downloadUrl = url; 24 this.fileName = name; 25 this.lastver = ver; 26 this.size = size; 27 this.version = versionid; 28 } 29 #endregion 30 }View Code
ConstFile.cs
一些常量配置文件
CommonUnitity.cs
//主要是更改获取多层目录文件夹路径
public static string GetFolderUrl(DownloadFileInfo file) { string folderPathUrl = string.Empty; int folderPathPoint = file.DownloadUrl.IndexOf("/", 15) + 1; string filepathstring = file.DownloadUrl.Substring(folderPathPoint); //int folderPathPoint1 = filepathstring.IndexOf("/"); //string filepathstring1 = filepathstring.Substring(folderPathPoint1 + 1); //if(filepathstring1.IndexOf("/") != -1) if(filepathstring.IndexOf("/") != -1) { //string[] ExeGroup = filepathstring1.Split('/'); string[] ExeGroup = filepathstring.Split('/'); for (int i = 0; i < ExeGroup.Length - 1; i++) { folderPathUrl += "\\" + ExeGroup[i]; } if (!Directory.Exists(SystemBinUrl + ConstFile.TEMPFOLDERNAME + folderPathUrl)) { Directory.CreateDirectory(SystemBinUrl + ConstFile.TEMPFOLDERNAME + folderPathUrl); } } return folderPathUrl; }
autoupdater.cs
1 public class AutoUpdater : IAutoUpdater 2 { 3 #region The private fields 4 private Config config = null; 5 private bool bNeedRestart = false; 6 private bool bDownload = false; 7 List<DownloadFileInfo> downloadFileListTemp = null; 8 #endregion 9 10 #region The public event 11 public event ShowHandler OnShow; 12 #endregion 13 14 #region The constructor of AutoUpdater 15 public AutoUpdater() 16 { 17 config = Config.LoadConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConstFile.FILENAME)); 18 } 19 #endregion 20 21 #region The public method 22 public void Update() 23 { 24 if (!config.Enabled) 25 return; 26 27 Dictionary<string, RemoteFile> listRemotFile = ParseRemoteXml(config.ServerUrl); 28 List<DownloadFileInfo> downloadList = new List<DownloadFileInfo>(); 29 30 foreach (LocalFile file in config.UpdateFileList) 31 { 32 if (listRemotFile.ContainsKey(file.Path)) 33 { 34 RemoteFile rf = listRemotFile[file.Path]; 35 //Version v1 = new Version(rf.LastVer); 36 //Version v2 = new Version(file.LastVer); 37 //if (v1 > v2) 38 string v1 = rf.Verison; 39 string v2 = file.Version; 40 if (v1 != v2) 41 { 42 downloadList.Add(new DownloadFileInfo(rf.Url, rf.Path, rf.LastVer, rf.Size, rf.Verison)); 43 file.Path = rf.Path; 44 file.LastVer = rf.LastVer; 45 file.Size = rf.Size; 46 file.Version = rf.Verison; 47 if (rf.NeedRestart) 48 bNeedRestart = true; 49 50 bDownload = true; 51 } 52 53 listRemotFile.Remove(file.Path); 54 } 55 } 56 57 foreach (RemoteFile file in listRemotFile.Values) 58 { 59 downloadList.Add(new DownloadFileInfo(file.Url, file.Path, file.LastVer, file.Size, file.Verison)); 60 bDownload = true; 61 config.UpdateFileList.Add(new LocalFile(file.Path, file.LastVer, file.Size, file.Verison)); 62 if (file.NeedRestart) 63 bNeedRestart = true; 64 } 65 66 downloadFileListTemp = downloadList; 67 68 if (bDownload) 69 { 70 OperProcess op = new OperProcess(); 71 op.InitUpdateEnvironment(); 72 DownloadConfirm dc = new DownloadConfirm(downloadList); 73 74 if (this.OnShow != null) 75 this.OnShow(); 76 StartDownload(downloadList); 77 } 78 } 79 80 public void RollBack() 81 { 82 foreach (DownloadFileInfo file in downloadFileListTemp) 83 { 84 string tempUrlPath = CommonUnitity.GetFolderUrl(file); 85 string oldPath = string.Empty; 86 try 87 { 88 if (!string.IsNullOrEmpty(tempUrlPath)) 89 { 90 oldPath = Path.Combine(CommonUnitity.SystemBinUrl + tempUrlPath.Substring(1), file.FileName); 91 } 92 else 93 { 94 oldPath = Path.Combine(CommonUnitity.SystemBinUrl, file.FileName); 95 } 96 97 if (oldPath.EndsWith("_")) 98 oldPath = oldPath.Substring(0, oldPath.Length - 1); 99 100 MoveFolderToOld(oldPath + ".old", oldPath); 101 102 } 103 catch (Exception ex) 104 { 105 //log the error message,you can use the application's log code 106 } 107 } 108 } 109 110 111 #endregion 112 113 #region The private method 114 string newfilepath = string.Empty; 115 private void MoveFolderToOld(string oldPath, string newPath) 116 { 117 if (File.Exists(oldPath) && File.Exists(newPath)) 118 { 119 System.IO.File.Copy(oldPath, newPath, true); 120 } 121 } 122 123 private void StartDownload(List<DownloadFileInfo> downloadList) 124 { 125 DownloadProgress dp = new DownloadProgress(downloadList); 126 if (dp.ShowDialog() == DialogResult.OK) 127 { 128 // 129 if (DialogResult.Cancel == dp.ShowDialog()) 130 { 131 return; 132 } 133 //Update successfully 134 config.SaveConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConstFile.FILENAME)); 135 136 if (bNeedRestart) 137 { 138 //Delete the temp folder 139 Directory.Delete(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConstFile.TEMPFOLDERNAME), true); 140 141 MessageBox.Show(ConstFile.APPLYTHEUPDATE, ConstFile.MESSAGETITLE, MessageBoxButtons.OK, MessageBoxIcon.Information); 142 CommonUnitity.RestartApplication(); 143 } 144 } 145 } 146 147 private Dictionary<string, RemoteFile> ParseRemoteXml(string xml) 148 { 149 XmlDocument document = new XmlDocument(); 150 document.Load(xml); 151 152 Dictionary<string, RemoteFile> list = new Dictionary<string, RemoteFile>(); 153 foreach (XmlNode node in document.DocumentElement.ChildNodes) 154 { 155 list.Add(node.Attributes["path"].Value, new RemoteFile(node)); 156 } 157 158 return list; 159 } 160 #endregion 161 162 }View Code
OperProcess.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 7 namespace AutoUpdater 8 { 9 /// <summary> 10 /// 启动进程、关闭进程操作 11 /// </summary> 12 public class OperProcess 13 { 14 #region init update env 15 public void InitUpdateEnvironment() 16 { 17 if (IfExist("MainProgram")) 18 { 19 CloseExe("MainProgram"); 20 } 21 } 22 #endregion init update env 23 24 #region updated start process 25 public void StartProcess() 26 { 27 string path = System.Environment.CurrentDirectory; 28 if (!IfExist("MainProgram")) 29 { 30 StartExe(path, "MainProgram.exe"); 31 } 32 CloseExe("KnightsWarrior"); 33 } 34 35 #endregion 36 37 #region 启动进程、关闭进程、判断进程是否存在 38 //启动exe绝对路径 39 private void StartExe(string filePath, string fileName) 40 { 41 Process proc = new Process(); 42 proc.StartInfo.UseShellExecute = true;//是否使用操作系统外壳程序启动进程 43 44 proc.StartInfo.WorkingDirectory = filePath;//启动进程的初始目录 45 proc.StartInfo.FileName = fileName; 46 proc.Start(); 47 } 48 49 50 //exeName 关闭的exe进程名 51 private void CloseExe(string exeName) 52 { 53 Process[] arrPro = Process.GetProcessesByName(exeName); 54 foreach (Process pro in arrPro) 55 pro.Kill(); 56 } 57 //processName 进程名 58 private bool IfExist(string processName) 59 { 60 Process[] pro = Process.GetProcessesByName(processName); 61 return pro.Count() > 0; 62 } 63 #endregion 启动进程、关闭进程 64 } 65 }View Code
以上就是整体的自动更新程序核心代码。
下面是创建xml的程序代码:
代码是根据评论修改的如下:
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Xml; 10 using System.IO; 11 using System.Diagnostics; 12 13 namespace CreateXmlTools 14 { 15 public partial class FormMain : Form 16 { 17 public FormMain() 18 { 19 InitializeComponent(); 20 txtWebUrl.Text = "172.30.100.55:8011"; 21 txtWebUrl.ForeColor = Color.Gray; 22 } 23 24 //获取当前目录 25 //string currentDirectory = AppDomain.CurrentDomain.BaseDirectory; 26 string currentDirectory = System.Environment.CurrentDirectory; 27 //服务端xml文件名称 28 string serverXmlName = "AutoupdateService.xml"; 29 //更新文件URL前缀 30 string url = string.Empty; 31 32 void CreateXml() 33 { 34 //创建文档对象 35 XmlDocument doc = new XmlDocument(); 36 //创建根节点 37 XmlElement root = doc.CreateElement("updateFiles"); 38 //头声明 39 XmlDeclaration xmldecl = doc.CreateXmlDeclaration("1.0", "utf-8", null); 40 doc.AppendChild(xmldecl); 41 DirectoryInfo dicInfo = new DirectoryInfo(currentDirectory); 42 43 //调用递归方法组装xml文件 44 PopuAllDirectory(doc, root, dicInfo); 45 //追加节点 46 doc.AppendChild(root); 47 //保存文档 48 doc.Save(serverXmlName); 49 } 50 51 //递归组装xml文件方法 52 private void PopuAllDirectory(XmlDocument doc, XmlElement root, DirectoryInfo dicInfo) 53 { 54 foreach (FileInfo f in dicInfo.GetFiles()) 55 { 56 //排除当前目录中生成xml文件的工具文件 57 if (f.Name != "CreateXmlTools.exe" && f.Name != "AutoupdateService.xml") 58 { 59 string path = dicInfo.FullName.Replace(currentDirectory, "").Replace("\\", "/"); 60 string folderPath=string.Empty; 61 if (path != string.Empty) 62 { 63 folderPath = path.TrimStart('/') + "/"; 64 } 65 XmlElement child = doc.CreateElement("file"); 66 child.SetAttribute("path", folderPath + f.Name); 67 child.SetAttribute("url", url + path + "/" + f.Name); 68 child.SetAttribute("lastver", FileVersionInfo.GetVersionInfo(f.FullName).FileVersion); 69 child.SetAttribute("size", f.Length.ToString()); 70 child.SetAttribute("needRestart", "false"); 71 child.SetAttribute("version", Guid.NewGuid().ToString()); 72 root.AppendChild(child); 73 } 74 } 75 76 foreach (DirectoryInfo di in dicInfo.GetDirectories()) 77 PopuAllDirectory(doc, root, di); 78 } 79 80 private void btnCreate_Click(object sender, EventArgs e) 81 { 82 url = "http://" + txtWebUrl.Text.Trim(); 83 CreateXml(); 84 ReadXml(); 85 } 86 87 private void ReadXml() 88 { 89 string path="AutoupdateService.xml"; 90 rtbXml.ReadOnly = true; 91 if (File.Exists(path)) 92 { 93 rtbXml.Text = File.ReadAllText(path); 94 } 95 } 96 97 private void txtWebUrl_Enter(object sender, EventArgs e) 98 { 99 txtWebUrl.ForeColor = Color.Black; 100 if (txtWebUrl.Text.Trim() == "172.30.100.55:8011") 101 { 102 txtWebUrl.Text = string.Empty; 103 } 104 } 105 106 } 107 }View Code
由于我的主程序是被别人写死的(没有修改权限没有代码)所以我只能单独写更新程序,由用户打开我的更新程序调用exe的方式来处理
所以多了一个程序专门用来更新的
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using AutoUpdater; 10 using System.Net; 11 using System.Xml; 12 using KnightsWarriorAutoupdater; 13 14 namespace KnightsWarrior 15 { 16 public partial class UpdateForm : Form 17 { 18 public UpdateForm() 19 { 20 InitializeComponent(); 21 } 22 void InitCheckUpdate() 23 { 24 #region check and download new version program 25 bool bHasError = false; 26 IAutoUpdater autoUpdater = new KnightsWarriorAutoupdater.AutoUpdater(); 27 try 28 { 29 autoUpdater.Update(); 30 } 31 catch (WebException exp) 32 { 33 MessageBox.Show("服务器连接失败"); 34 bHasError = true; 35 } 36 catch (XmlException exp) 37 { 38 bHasError = true; 39 MessageBox.Show("下载更新文件错误"); 40 } 41 catch (NotSupportedException exp) 42 { 43 bHasError = true; 44 MessageBox.Show("升级文件配置错误"); 45 } 46 catch (ArgumentException exp) 47 { 48 bHasError = true; 49 MessageBox.Show("下载升级文件错误"); 50 } 51 catch (Exception exp) 52 { 53 bHasError = true; 54 MessageBox.Show("更新过程中出现错误"); 55 } 56 finally 57 { 58 if (bHasError == true) 59 { 60 try 61 { 62 autoUpdater.RollBack(); 63 } 64 catch (Exception) 65 { 66 //Log the message to your file or database 67 } 68 } 69 OperProcess op = new OperProcess(); 70 //启动进程 71 op.StartProcess(); 72 } 73 #endregion 74 } 75 } 76 }View Code
1、服务器端
1、CreateXmlTools.exe给发布dll人员使用,用来生成要升级文件的列表,放在更新文件中的。
2、webServer地址是web服务器的地址。
3、点击生成之后会生成一个文件名为AutoupdateService.xml文件。
4、将生成的xml文件放置在web服务器的根目录里。
2、客户端
1、AutoUpdater.Config,该文件是保证客户端更新程序调用获取更新文件列表时使用的。
2、KnightsWarrior.exe更新主程序,用户直接调用该文件
3、AutoUpdater.dll更新程序的核心程序
本程序是要结合web服务器使用的,所有要更新的文件需要放在搭建的web服务器上,按照对应的目录存放,点CreateXmlTools.exe会生成一个xml文件的。
以上就是所有修改和增加部分的程序的代码及一些简要说明。建议先去看看组件源码,圣殿骑士大哥将其托管在托管地址。欢迎拍砖!