Loading... # C# 實現客戶端程序自動更新 看到一篇不錯的帖子,可能以后會用到,果斷收藏 文章來源 博客園 jenry(云飛揚)http://www.cnblogs.com/jenry/archive/2006/08/15/477302.html 由于微軟提供的更新程序使用不方便,所以又寫了此程序。此程序是本人一年前所寫的一段程序,當時在開發一個CS版本報價系統,當時由于開發過程倉促,代碼可能有點不是太規范此程序編譯后只有一下AutoUpdate.exe文件與一個配置文件UpdateList.xml,主要通過本地程序與服務端程序文件的版本號來升級與更新本地程序文件。   UpdateList.xml文件內容如下: <?xml version="1.0" encoding="gb2312"?><AutoUpdater> `<description>`Application autoUpdate `</description>` `<Updater>` `<Url>`http://10.0.5.98/SoftUpdate/`</Url>` `<LastUpdateTime>`2005-09-05 `</LastUpdateTime>` `</Updater>` `<Application applicationId="ItemSoft">` `<EntryPoint>`ItemSoft.exe `</EntryPoint>` `<Location>`.`</Location>` `<Version>`1.0.0.0 `</Version>` `</Application>` `<Files>` `<File Ver="1.0.0.0" Name="ItemSoft.exe" />` `<File Ver="1.0.0.0" Name="Reports\test.txt"/>` `<File Ver="1.0.0.0" Name="Interop.grproLib.dll"/>` `<File Ver="1.0.0.0" Name="Reports\test.grf"/>` `</Files>` `</AutoUpdater>` 說明: `<description></description>` 程序的描述; `<Url></Url>` 更新服務器地址,為一個虛擬目錄或站點路徑; `<EntryPoint></EntryPoint>` 需要更新主程序文件,為exe; `<Location>`.`</Location>` 需要更新主程序文件所在路徑; `<Version>`1.0.0.0 `</Version>` 主程序版本號; `<Files>` `</Files>` 需要更新的文件列表;Ver:文件版本號,Name:文件名,包括路徑(相對); 使用時,在更新服務器上新建Web虛擬目錄或站點,然后將需要更新的文件與UpdateList.xml放在上面; 本地將AutoUpdate.exe與UpdateList.xml放在主程序的根目錄下。本地UpdateList.xml中的版本號如果小于服務端,自動程序會自動下載所需要更新的文件。 本人的完整.Net打包程序下載中所帶的項目中,就使用了此更新程序。 源碼下載:[/Files/jenry/AutoUpdate.rar](https://files.cnblogs.com/jenry/AutoUpdate.rar) 二進制文件下載:[/Files/jenry/bin.rar](https://files.cnblogs.com/jenry/bin.rar) 生成配置文件UpdateList.xml工具:[/Files/jenry/AULWriter1.0.rar](https://files.cnblogs.com/jenry/AULWriter1.0.rar) (由whatisgood 提供,感謝?。。㎞ew --- # [c#自動更新程序](https://www.cnblogs.com/keguoquan/p/14542631.html) **主要功能介紹** 實現文件的自動更新。主要功能: 1. 支持整包完全更新,即客戶端只需輸入一個服務器地址,即可下載所有文件。 2. 支持增量更新,即只更新指定的某幾個文件。 3. 支持自動更新程序的更新 更新界面如圖:  客戶端 [](javascript:void(0); "復制代碼") ``` /// <summary> /// 應用程序的主入口點。 /// </summary> [STAThread] static void Main() { //在主程序中 更新替換自動升級程序 //ReplaceAutoUpgrade(); bool isEnterMain = false; try { //設置默認更新地址,如果不設置,后面會從配置文件,或界面上進行設置 UpgradeHelper.Instance.DefaultUrl = "http://localhost:17580"; if (UpgradeHelper.Instance.Local_UpgradeModel != null) { UpgradeHelper.Instance.UpgradeUrl = UpgradeHelper.Instance.Local_UpgradeModel.UpgradeUrl; } if (UpgradeHelper.Instance.WillUpgrades.Count == 0 && UpgradeHelper.Instance.Local_UpgradeModel != null) { //沒有待更新,并且本地版本信息文件不為空,則直接啟動主程序 bool isSucced = UpgradeHelper.StartRunMain(UpgradeHelper.Instance.Local_UpgradeModel.RunMain); if (isSucced) { Application.Exit(); } else { //清理版本信息 以便重新檢測版本 UpgradeHelper.Instance.ClearUpgradeModel(); isEnterMain = true; } } else { isEnterMain = true; } } catch (Exception ex) { isEnterMain = true; MessageBox.Show("運行更新程序異常:\n" + ex.Message, "錯誤提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } if (isEnterMain) { //進入更新主界面 Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new FrmUpdate()); } } ``` [](javascript:void(0); "復制代碼") [](javascript:void(0); "復制代碼") ``` 1 public partial class FrmUpdate : Form 2 { 3 /// <summary> 4 /// 構造函數 5 /// </summary> 6 /// <param name="tempPath"></param> 7 /// <param name="updateFiles"></param> 8 public FrmUpdate() 9 { 10 InitializeComponent(); 11 } 12 13 /// <summary> 14 /// 窗體加載事件 15 /// </summary> 16 /// <param name="sender"></param> 17 /// <param name="e"></param> 18 private void FrmUpdate_Load(object sender, EventArgs e) 19 { 20 try 21 { 22 //加載服務器地址 23 txtHostUrl.Text = UpgradeHelper.Instance.UpgradeUrl; 24 BeginUpgrade(); 25 } 26 catch (Exception ex) 27 { 28 Output("初始化異常:" + ex.Message); 29 } 30 } 31 32 /// <summary> 33 /// 手動更新 34 /// </summary> 35 /// <param name="sender"></param> 36 /// <param name="e"></param> 37 private void butBegin_Click(object sender, EventArgs e) 38 { 39 try 40 { 41 if (string.IsNullOrWhiteSpace(txtHostUrl.Text)) 42 { 43 Output("請先輸入服務器地址!"); 44 return; 45 } 46 UpgradeHelper.Instance.UpgradeUrl = txtHostUrl.Text.Trim(); 47 //清理版本信息 以便重新檢測版本 48 UpgradeHelper.Instance.ClearUpgradeModel(); 49 BeginUpgrade(); 50 } 51 catch (Exception ex) 52 { 53 Output("更新異常:" + ex.Message); 54 } 55 } 56 57 private void BeginUpgrade() 58 { 59 try 60 { 61 if (string.IsNullOrWhiteSpace(UpgradeHelper.Instance.UpgradeUrl)) 62 { 63 return; 64 } 65 if (!(UpgradeHelper.Instance.UpgradeUrl.StartsWith("http://") || UpgradeHelper.Instance.UpgradeUrl.StartsWith("https://"))) 66 { 67 Output("錯誤的服務器地址,地址必須以http://或者https://開頭"); 68 return; 69 } 70 //判斷是否有更新 71 if (UpgradeHelper.Instance.WillUpgrades.Count > 0 && UpgradeHelper.Instance.Server_UpgradeModel != null) 72 { 73 SetWinControl(false); 74 //殺死主進程 75 UpgradeHelper.KillProcess(UpgradeHelper.Instance.Server_UpgradeModel.RunMain); 76 RunUpgrade();//啟動更新 77 } 78 } 79 catch (Exception ex) 80 { 81 Output("更新異常:" + ex.Message); 82 } 83 } 84 85 /// <summary> 86 /// 啟動更新 87 /// </summary> 88 private void RunUpgrade() 89 { 90 //啟動更新 91 SetCaption(string.Format("共需更新文件{0}個,已更新0個。正在更新下列文件:", UpgradeHelper.Instance.WillUpgrades.Count)); 92 Task.Factory.StartNew(() => 93 { 94 string curFile = ""; 95 try 96 { 97 int idx = 0; 98 foreach (KeyValuePair<string, string> item in UpgradeHelper.Instance.WillUpgrades) 99 { 100 curFile = item.Key; 101 string filePath = string.Format("{0}\\{1}", Application.StartupPath, item.Key); 102 if (item.Key.IndexOf(UpgradeHelper.Instance.Server_UpgradeModel.AutoUpgrade) >= 0) 103 { 104 //如果當前文件為更新主程序 105 filePath = string.Format("{0}\\AutoUpgradeTemp\\{1}", Application.StartupPath, item.Key); 106 } 107 string directory = Path.GetDirectoryName(filePath); 108 if (!Directory.Exists(directory)) 109 { 110 Directory.CreateDirectory(directory); 111 } 112 MyWebResquest.DownloadFile(UpgradeHelper.Instance.UpgradeUrl, item.Key, filePath); 113 idx++; 114 SetCaption(string.Format("共需更新文件{0}個,已更新{1}個。更新文件列表:", UpgradeHelper.Instance.WillUpgrades.Count, idx)); 115 Output(string.Format("更新文件{0}完成", curFile)); 116 } 117 //保存版本文件 118 File.WriteAllText(UpgradeHelper.Instance.Local_UpgradeXmlPath, UpgradeHelper.Instance.Server_UpgradeXml); 119 120 SetCaption(string.Format("更新完成,共更新文件{0}個", UpgradeHelper.Instance.WillUpgrades.Count)); 121 Output(string.Format("更新完成,共更新文件{0}個", UpgradeHelper.Instance.WillUpgrades.Count)); 122 123 //下載完成后處理 124 UpgradeHelper.StartRunMain(UpgradeHelper.Instance.Server_UpgradeModel.RunMain); 125 126 //退出當前程序 127 ExitCurrent(); 128 } 129 catch (Exception ex) 130 { 131 Output(string.Format("更新文件{0}異常:{1}", curFile, ex.Message)); 132 SetWinControl(true); 133 } 134 }); 135 } 136 137 /// <summary> 138 /// 設置界面控件是否可用 139 /// </summary> 140 /// <param name="enabled"></param> 141 private void SetWinControl(bool enabled) 142 { 143 if (this.InvokeRequired) 144 { 145 Action<bool> d = new Action<bool>(SetWinControl); 146 this.Invoke(d, enabled); 147 } 148 else 149 { 150 txtHostUrl.Enabled = enabled; 151 butBegin.Enabled = enabled; 152 } 153 } 154 155 /// <summary> 156 /// 退出當前程序 157 /// </summary> 158 private void ExitCurrent() 159 { 160 if (this.InvokeRequired) 161 { 162 Action d = new Action(ExitCurrent); 163 this.Invoke(d); 164 } 165 else 166 { 167 Application.Exit(); 168 } 169 } 170 171 #region 日志輸出 172 173 /// <summary> 174 /// 設置跟蹤狀態 175 /// </summary> 176 /// <param name="caption"></param> 177 private void SetCaption(string caption) 178 { 179 if (this.lblCaption.InvokeRequired) 180 { 181 Action<string> d = new Action<string>(SetCaption); 182 this.Invoke(d, caption); 183 } 184 else 185 { 186 this.lblCaption.Text = caption; 187 } 188 } 189 190 /// <summary> 191 /// 設置跟蹤狀態 192 /// </summary> 193 /// <param name="caption"></param> 194 private void Output(string log) 195 { 196 if (this.txtLog.InvokeRequired) 197 { 198 Action<string> d = new Action<string>(Output); 199 this.Invoke(d, log); 200 } 201 else 202 { 203 txtLog.AppendText(string.Format("{0}:{1}\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), log)); 204 txtLog.ScrollToCaret(); 205 } 206 } 207 208 private void ClearOutput() 209 { 210 if (this.txtLog.InvokeRequired) 211 { 212 Action d = new Action(ClearOutput); 213 this.Invoke(d); 214 } 215 else 216 { 217 txtLog.Text = ""; 218 } 219 } 220 221 #endregion 222 223 private void FrmUpdate_FormClosing(object sender, FormClosingEventArgs e) 224 { 225 if (e.CloseReason == CloseReason.UserClosing) 226 { 227 if (MessageBox.Show("升級未完成,退出后將導致軟件無法正常使用,你確定要退出嗎?", "退出提示", MessageBoxButtons.YesNo) != System.Windows.Forms.DialogResult.Yes) 228 { 229 //取消"關閉窗口"事件 230 e.Cancel = true; 231 } 232 } 233 } 234 } ``` [](javascript:void(0); "復制代碼") [](javascript:void(0); "復制代碼") ``` 1 /// <summary> 2 /// 更新幫助類 3 /// </summary> 4 public class UpgradeHelper 5 { 6 /// <summary> 7 /// 默認服務器地址 8 /// 在配置文件中未找到地址時,使用此地址進行更新 9 /// </summary> 10 public string DefaultUrl { get; set; } 11 12 public string _upgradeUrl; 13 /// <summary> 14 /// 獲取或設置服務器地址 15 /// </summary> 16 public string UpgradeUrl 17 { 18 get 19 { 20 if (string.IsNullOrWhiteSpace(_upgradeUrl)) 21 { 22 return DefaultUrl; 23 } 24 return _upgradeUrl; 25 } 26 set 27 { 28 _upgradeUrl = value; 29 } 30 } 31 32 /// <summary> 33 /// 本地配置文件路徑 34 /// </summary> 35 public string Local_UpgradeXmlPath = Path.Combine(Application.StartupPath, "UpgradeList.xml"); 36 37 private UpgradeModel _local_UpgradeModel; 38 /// <summary> 39 /// 本地版本信息 40 /// </summary> 41 public UpgradeModel Local_UpgradeModel 42 { 43 get 44 { 45 try 46 { 47 if (_local_UpgradeModel == null) 48 { 49 if (File.Exists(Local_UpgradeXmlPath)) 50 { 51 _local_UpgradeModel = new UpgradeModel(); 52 _local_UpgradeModel.LoadUpgrade(File.ReadAllText(Local_UpgradeXmlPath)); 53 } 54 } 55 return _local_UpgradeModel; 56 } 57 catch (Exception ex) 58 { 59 throw new Exception(string.Format("獲取本地版本文件UpgradeList.xml異常:{0}", ex.Message)); 60 } 61 } 62 } 63 64 private UpgradeModel _server_UpgradeModel; 65 /// <summary> 66 /// 服務器版本信息 67 /// </summary> 68 public UpgradeModel Server_UpgradeModel 69 { 70 get 71 { 72 try 73 { 74 if (_server_UpgradeModel == null && !string.IsNullOrWhiteSpace(UpgradeUrl)) 75 { 76 string resXml = MyWebResquest.GetUpgradeList(UpgradeUrl); 77 if (!string.IsNullOrWhiteSpace(resXml)) 78 { 79 _server_UpgradeModel = new UpgradeModel(); 80 _server_UpgradeModel.LoadUpgrade(resXml); 81 _server_UpgradeXml = resXml; 82 } 83 } 84 return _server_UpgradeModel; 85 } 86 catch (Exception ex) 87 { 88 throw new Exception(string.Format("獲取服務端版本文件UpgradeList.xml異常:{0}", ex.Message)); 89 } 90 } 91 } 92 93 private string _server_UpgradeXml; 94 /// <summary> 95 /// 服務端版本配置xml 96 /// </summary> 97 public string Server_UpgradeXml 98 { 99 get 100 { 101 return _server_UpgradeXml; 102 } 103 } 104 105 private Dictionary<string, string> _willUpgrades; 106 /// <summary> 107 /// 待更新文件列表,如果為0,則表示不需要更新 108 /// </summary> 109 public Dictionary<string, string> WillUpgrades 110 { 111 get 112 { 113 if (_willUpgrades == null) 114 { 115 _willUpgrades = new Dictionary<string, string>(); 116 //如果服務器端未獲取到版本信息 則不更新 117 if (Server_UpgradeModel != null) 118 { 119 if (Local_UpgradeModel == null)//本地版本信息為空 全部更新 120 { 121 _willUpgrades = Server_UpgradeModel.DictFiles; 122 } 123 else 124 { 125 //對比需要更新的文件 126 foreach (var item in Server_UpgradeModel.DictFiles) 127 { 128 //如果找到 129 if (Local_UpgradeModel.DictFiles.ContainsKey(item.Key)) 130 { 131 //如果版本不匹配 132 if (Local_UpgradeModel.DictFiles[item.Key] != item.Value) 133 { 134 _willUpgrades.Add(item.Key, item.Value); 135 } 136 } 137 else 138 { 139 //沒有找到 140 _willUpgrades.Add(item.Key, item.Value); 141 } 142 } 143 } 144 } 145 } 146 return _willUpgrades; 147 } 148 } 149 150 /// <summary> 151 /// 清空版本信息 152 /// </summary> 153 public void ClearUpgradeModel() 154 { 155 if (File.Exists(Local_UpgradeXmlPath)) 156 { 157 158 try 159 { 160 string xmlStr = File.ReadAllText(Local_UpgradeXmlPath); 161 XmlDocument xmlDoc = new XmlDocument(); 162 xmlDoc.LoadXml(xmlStr); 163 164 XmlNode node = xmlDoc.SelectSingleNode("Upgrade/Files"); 165 if (node != null && node.ChildNodes.Count > 0) 166 { 167 node.RemoveAll(); 168 } 169 File.WriteAllText(UpgradeHelper.Instance.Local_UpgradeXmlPath, xmlDoc.InnerXml); 170 } 171 catch (Exception) 172 { } 173 } 174 _local_UpgradeModel = null; 175 _server_UpgradeModel = null; 176 _willUpgrades = null; 177 } 178 179 180 181 #region 單例對象 182 183 private static UpgradeHelper _instance; 184 /// <summary> 185 /// 單例對象 186 /// </summary> 187 public static UpgradeHelper Instance 188 { 189 get 190 { 191 if (_instance == null) 192 { 193 _instance = new UpgradeHelper(); 194 //初始化本地配置文件,以及服務器地址 195 if (_instance.Local_UpgradeModel != null) 196 { 197 _instance.UpgradeUrl = _instance.Local_UpgradeModel.UpgradeUrl; 198 } 199 } 200 return _instance; 201 } 202 } 203 204 #endregion 205 206 #region 靜態方法 207 208 /// <summary> 209 /// 啟動主程序 210 /// </summary> 211 /// <param name="fileName"></param> 212 public static bool StartRunMain(string fileName) 213 { 214 string fullPath = fileName; 215 try 216 { 217 Process process = GetProcess(fileName); 218 if (process != null)//以及存在運行中的主進程 219 { 220 return true; 221 } 222 fullPath = string.Format("{0}\\{1}", Application.StartupPath, fileName); 223 224 ProcessStartInfo main = new ProcessStartInfo(fullPath); 225 Process.Start(fullPath); 226 return true; 227 } 228 catch (Exception ex) 229 { 230 MessageBox.Show(string.Format("主程序{0}調用失?。篭n{1}", fullPath, ex.Message), "錯誤提示", MessageBoxButtons.OK, MessageBoxIcon.Error); 231 } 232 return false; 233 } 234 235 /// <summary> 236 /// 殺死進程 237 /// </summary> 238 /// <param name="process"></param> 239 public static void KillProcess(string processName) 240 { 241 if (string.IsNullOrWhiteSpace(processName)) return; 242 processName = processName.ToLower(); 243 processName = processName.Replace(".exe", ""); 244 //殺死主進程 245 Process[] processes = Process.GetProcesses(); 246 foreach (Process process in processes) 247 { 248 if (!string.IsNullOrWhiteSpace(process.ProcessName)) 249 { 250 if (process.ProcessName.ToLower() == processName) 251 { 252 process.Kill(); 253 } 254 } 255 } 256 } 257 258 /// <summary> 259 /// 獲取進程 260 /// </summary> 261 /// <param name="pName"></param> 262 /// <returns></returns> 263 public static Process GetProcess(string pName) 264 { 265 if (string.IsNullOrWhiteSpace(pName)) return null; 266 pName = pName.ToLower(); 267 pName = pName.Replace(".exe", ""); 268 //殺死主進程 269 Process[] processes = Process.GetProcesses(); 270 foreach (Process process in processes) 271 { 272 if (!string.IsNullOrWhiteSpace(process.ProcessName)) 273 { 274 if (process.ProcessName.ToLower() == pName) 275 { 276 return process; 277 } 278 } 279 } 280 return null; 281 } 282 283 #endregion 284 285 } ``` [](javascript:void(0); "復制代碼") [](javascript:void(0); "復制代碼") ``` 1 public class UpgradeModel 2 { 3 /// <summary> 4 /// 初始化對象 5 /// </summary> 6 /// <param name="xml"></param> 7 public void LoadUpgrade(string xml) 8 { 9 XmlDocument xmlDoc = new XmlDocument(); 10 xmlDoc.LoadXml(xml); 11 12 //讀取UpgradeUrl 13 XmlNode node = xmlDoc.SelectSingleNode("//UpgradeUrl"); 14 if (node != null) 15 { 16 this.UpgradeUrl = node.InnerText; 17 } 18 //讀取RunMain 19 node = xmlDoc.SelectSingleNode("//RunMain"); 20 if (node != null) 21 { 22 this.RunMain = node.InnerText; 23 } 24 //讀取RunMain 25 node = xmlDoc.SelectSingleNode("//AutoUpgrade"); 26 if (node != null) 27 { 28 this.AutoUpgrade = node.InnerText; 29 } 30 //讀取Files 31 node = xmlDoc.SelectSingleNode("Upgrade/Files"); 32 this.DictFiles = new Dictionary<string, string>(); 33 if (node != null && node.ChildNodes.Count > 0) 34 { 35 foreach (XmlNode item in node.ChildNodes) 36 { 37 if (item.Name != "#comment") 38 { 39 string name = GetNodeAttrVal(item, "Name"); 40 string version = GetNodeAttrVal(item, "Version"); 41 if (!this.DictFiles.ContainsKey(name)) 42 { 43 this.DictFiles.Add(name, version); 44 } 45 } 46 } 47 } 48 } 49 50 private string GetNodeAttrVal(XmlNode node, string attr) 51 { 52 if (node != null && node.Attributes != null && node.Attributes[attr] != null) 53 { 54 string val = node.Attributes[attr].Value; 55 if (!string.IsNullOrWhiteSpace(val)) 56 { 57 return val.Trim(); 58 } 59 return val; 60 } 61 return string.Empty; 62 } 63 64 /// <summary> 65 /// 服務器地址 66 /// </summary> 67 public string UpgradeUrl { get; set; } 68 69 /// <summary> 70 /// 更新完成后運行的主程序名稱 71 /// </summary> 72 public string RunMain { get; set; } 73 74 /// <summary> 75 /// 更新程序名稱 76 /// </summary> 77 public string AutoUpgrade { get; set; } 78 79 /// <summary> 80 /// 文件列表 81 /// string 文件名 82 /// string 版本號 83 /// </summary> 84 public Dictionary<string, string> DictFiles { get; set; } 85 } ``` [](javascript:void(0); "復制代碼") 服務端 服務端主Xml版本文件,包含所有的項目文件,客戶端根據每個文件的版本號進行判斷是否需要更新。如果需只更新某幾個文件,則將對應文件的版本號更改只更高的版本號即可 [](javascript:void(0); "復制代碼") ``` 1 <?xml version="1.0" encoding="utf-8" ?> 2 <Upgrade> 3 <!--服務器地址--> 4 <UpgradeUrl>http://localhost:17580</UpgradeUrl> 5 <!--更新完成后運行的主程序名稱--> 6 <RunMain>ClientMain.exe</RunMain> 7 <!--更新程序名稱--> 8 <AutoUpgrade>AutoUpgrade.exe</AutoUpgrade> 9 <Files> 10 <!--更新文件列表,以Version為標志,當Version改變時,客戶端啟動會自動更新。子路徑格式:\image\index.jpg--> 11 <File Version="01" Name="\image\index.jpg" /> 12 <File Version="01" Name="ClientMain.exe" /> 13 <File Version="01" Name="AutoUpgrade.exe" /> 14 </Files> 15 </Upgrade> ``` [](javascript:void(0); "復制代碼") 服務端主要提供連個可以通過Http的get或post訪問的路徑。一個用于獲取版本Xml文件內容,一個用于下載指定文件的路徑。以下代碼示例通過asp.net mvc進行實現。大家可以根據自己技術方式參照實現。 [](javascript:void(0); "復制代碼") ``` 1 /// <summary> 2 /// 自動升級服務 3 /// </summary> 4 public class UpgradeController : Controller 5 { 6 // 7 // GET: /Upgrade/ 8 9 /// <summary> 10 /// 獲取更新文件列表 11 /// </summary> 12 /// <returns></returns> 13 public object UpgradeList() 14 { 15 string cacheKey = "Upgrade_UpgradeList.xml"; 16 string resStr = CommonLibrary.CacheClass.GetCache<string>(cacheKey); 17 if (string.IsNullOrWhiteSpace(resStr)) 18 { 19 string fileName = Server.MapPath(@"~\App_Data\UpgradeList.xml"); 20 if (System.IO.File.Exists(fileName)) 21 { 22 resStr = System.IO.File.ReadAllText(fileName); 23 CommonLibrary.CacheClass.SetCacheMins(cacheKey, resStr, 1); 24 } 25 } 26 return resStr; 27 } 28 29 /// <summary> 30 /// 生成更新文件 31 /// </summary> 32 /// <returns></returns> 33 public object Create() 34 { 35 UpgradeFileManager.CreateFiles(Server.MapPath("/App_Data")); 36 return "ok"; 37 } 38 39 /// <summary> 40 /// 下載文件 41 /// </summary> 42 /// <param name="fileName"></param> 43 /// <returns></returns> 44 public object DownloadFile() 45 { 46 string fileName = PageRequest.GetString("fileName"); 47 fileName = Server.MapPath(string.Format(@"~\App_Data\{0}", fileName)); 48 return File(fileName, "application/octet-stream"); 49 } 50 51 /// <summary> 52 /// 異常處理 53 /// </summary> 54 /// <param name="filterContext"></param> 55 protected override void OnException(ExceptionContext filterContext) 56 { 57 filterContext.HttpContext.Response.StatusCode = 400; 58 filterContext.Result = Content(filterContext.Exception.GetBaseException().Message); 59 filterContext.ExceptionHandled = true; 60 } 61 } ``` [](javascript:void(0); "復制代碼") [](javascript:void(0); "復制代碼") ``` 1 /// <summary> 2 /// 此類主要作用,對于項目文件非常多,自己手動編輯很麻煩,可以采用此方法,指定目錄自動生成初始化的版本文件 3 /// </summary> 4 public class UpgradeFileManager 5 { 6 /// <summary> 7 /// 創建版本文件 8 /// </summary> 9 /// <param name="path"></param> 10 public static void CreateFiles(string path) 11 { 12 List<string> dirList = new List<string>(); 13 GetAllDirt(path, dirList);//獲取所有目錄 14 dirList.Add(path); 15 System.Text.StringBuilder xml = new System.Text.StringBuilder(); 16 xml.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>"); 17 xml.AppendLine(" <Files>"); 18 foreach (var diry in dirList) 19 { 20 string[] files = Directory.GetFiles(diry); 21 foreach (string filePath in files) 22 { 23 FileInfo info = new FileInfo(filePath); 24 string name = filePath.Replace(path, ""); 25 if (info.Directory.FullName == path) 26 { 27 name = name.Remove(0, 1); 28 } 29 xml.AppendLine(string.Format(" <File Version=\"1\" Name=\"{0}\" />", name)); 30 } 31 } 32 xml.AppendLine("</Files>"); 33 using (StreamWriter sw = new StreamWriter(Path.Combine(path, "UpgradeList_Temp.xml"))) 34 { 35 sw.Write(xml); 36 sw.Close(); 37 } 38 } 39 40 /// <summary> 41 /// 獲取所有子目錄 42 /// </summary> 43 /// <param name="curDir"></param> 44 /// <param name="list"></param> 45 private static void GetAllDirt(string curDir, List<string> list) 46 { 47 string[] dirs = Directory.GetDirectories(curDir); 48 if (dirs.Length > 0) 49 { 50 foreach (string item in dirs) 51 { 52 list.Add(item); 53 GetAllDirt(item, list); 54 } 55 } 56 } 57 } ``` [](javascript:void(0); "復制代碼") # 結語 源代碼托管于GitHub,供大伙學習參考,項目地址:[https://github.com/keguoquan/AutoUpgrade](https://github.com/keguoquan/AutoUpgrade)。感興趣或覺得不錯的望賞個star,不勝感激! 若能順手點個贊,更加感謝! 如有疑問可以QQ咨詢:343798739 --- # [C# Winform 自動更新程序實例](https://www.cnblogs.com/garychk/p/7978710.html) 第一步:檢查更新 檢查更新其實無非就是去比較更新包的版本和本地軟件版本,如果高則更新、低則不更新。怎么獲取版本號方法很多,本案例是獲取軟件的配置文件。 [](javascript:void(0); "復制代碼") ``` private bool CheckUpdate() { bool result = false; try { string Cfg = TxtRead(exePath "\\Config.txt"); ConfigLocal = JsonConvert.DeserializeObject<DTO_Config>(Cfg); CheckUpdateURL = ConfigLocal.AutoUpdateURL; Cfg = TxtRead(CheckUpdateURL "\\Config.txt"); ConfigRemote = JsonConvert.DeserializeObject<DTO_Config>(Cfg); VersionR = ConfigRemote.Version; VersionL = ConfigLocal.Version; int VersionRemote = int.Parse(ConfigRemote.Version.Replace(".", "")); int VersionLocal = int.Parse(ConfigLocal.Version.Replace(".", "")); result = VersionRemote > VersionLocal; } catch { } return result; } ``` [](javascript:void(0); "復制代碼") 第二步:下載更新包 因為C/S的軟件更新是面對所有用戶,S端除了給C端提供基本的服務外,還可以給C端提供更新包。而這個S端可以是網絡上的一個固定地址,也可以是局域網內一個公共盤。那下載更新包無非就是去訪問服務端的文件,然后Copy下來或下載下來。下面給出訪問網絡和訪問局域網兩個案例: A、訪問遠程網絡地址這里采用的是WebClient [](javascript:void(0); "復制代碼") ``` public void DownLoadFile() { if (!Directory.Exists(UpdateFiles)) { Directory.CreateDirectory(UpdateFiles); } using (WebClient webClient = new WebClient()) { try { webClient.DownloadFileCompleted = new AsyncCompletedEventHandler(client_DownloadFileCompleted); webClient.DownloadProgressChanged = new DownloadProgressChangedEventHandler(client_DownloadProgressChanged); webClient.DownloadFileAsync(new Uri(CheckUpdateURL "\\UpdateFile.rar"), UpdateFiles "\\UpdateFile.rar"); } catch (WebException ex) { MessageBox.Show(ex.Message, "系統提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } ``` [](javascript:void(0); "復制代碼") 這里面應用到兩個方法,DownloadProgressChanged,監聽異步下載的進度;DownloadFileCompleted,監聽完成異步文件下載; [](javascript:void(0); "復制代碼") ``` private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { this.progressBarUpdate.Minimum = 0; this.progressBarUpdate.Maximum = (int)e.TotalBytesToReceive; this.progressBarUpdate.Value = (int)e.BytesReceived; this.lblPercent.Text = e.ProgressPercentage "%"; } ``` [](javascript:void(0); "復制代碼") [](javascript:void(0); "復制代碼") ``` private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show(e.Error.Message, "系統提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { this.lblMessage.Text = "下載完成"; //復制更新文件替換舊文件 DirectoryInfo TheFolder = new DirectoryInfo(UpdateFiles); foreach (FileInfo NextFile in TheFolder.GetFiles()) { File.Copy(NextFile.FullName, Application.StartupPath NextFile.Name, true); } } } ``` [](javascript:void(0); "復制代碼") B、訪問服務端公共盤,直接采用File.Copy [](javascript:void(0); "復制代碼") ``` public void GetRemoteFile() { try { DirectoryInfo TheFolder = new DirectoryInfo(CheckUpdateURL); FileInfo[] FileList = TheFolder.GetFiles(); this.progressBarUpdate.Minimum = 0; this.progressBarUpdate.Maximum = FileList.Length; foreach (FileInfo NextFile in FileList) { if (NextFile.Name != "Config.txt") { File.Copy(NextFile.FullName, exePath "\\" NextFile.Name, true); } this.lblMessage.Text = "更新" NextFile.Name; this.progressBarUpdate.Value = 1; this.lblPercent.Text = "更新進度... " (this.progressBarUpdate.Value / FileList.Length) * 100 "%"; } this.lblMessage.Text = "更新完成"; //更改本地版本號為最新版本號 ConfigLocal.Version = VersionR; string cfgs = JsonConvert.SerializeObject(ConfigLocal); TxtWrite(Application.StartupPath "\\Config.txt", cfgs); } catch (Exception ex) { MessageBox.Show(ex.Message, "系統提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } } ``` [](javascript:void(0); "復制代碼") 第三步:替換本地文件 這一步或許在第二步中已經實現了,如果你采用的是File.Copy。替換也就是復制粘貼的問題。采用WebClient下載了zip包,那還需解壓一下壓縮包然后再File.Copy。 --- # [C#實現之(自動更新)](https://www.cnblogs.com/yelanggu/p/9288149.html) 做開發的人,尤其是做客戶端(C/S)系統開發的人都會遇到一個頭疼的問題,就是軟件的自動更新;系統發布后怎樣自動的更新程序,在下有幸開發過一個自動更新程序,更新程序與任何宿主程序是完全獨立的;只要在主程序里面啟動更新程序就行了;更新程序也是一個可執行文件,在啟動的時候可以設置是否是自動更新和是否是手動更新,自動更新的意思就是說不需要人工的干預實現從遠程服務器下載更新包,而如果是手動更新就會涉及到用戶點擊程序中的按鈕實現更新;在自動更新與手動更新中可以根據項目的需要進行選擇,有的程序必須要求用戶進行更新才能繼續使用,所以程序自動更新是有必要的;手動更新就是用戶可以隨時更新程序,不需要嚴格的控制版本問題;下面本人就來講一下具體的實現細節,我貼出部分代碼,源碼屬公司財產本人不宜上傳; 自動更新的目的就是將服務器上的DLL文件拷貝到本地執行目錄中,并且覆蓋本地同名的文件;流程很簡單,但是實現起來有幾個地方需要注意: 1.大批量的DLL文件怎么下載到本地來,有多個DLL文件在下載過程中如果網速慢的情況下可能出現丟包、丟文件等情況;本人的實現是將多個文件通過ICSharpCode.SharpZipLib組件進行打包,這樣可以省很多事;(如:動態連接庫文件dll的名稱在傳輸過程中大小寫可能會變化) 2.下載到本地了,怎么覆蓋原有的同名文件;本人的實現是先同名的文件的支持刪除,然后解壓縮;這個過程需要臨時保存刪除的文件,防止操作失敗程序無法啟動,要注意有事務性的原理; 3.如果更新的文件不只是單單的DLL文件可能還有一些無限極的文件夾;本人的實現是如果存在同名的文件夾,直接遞歸的刪除,然后將其解壓縮到目錄中;由于壓縮包解壓后的頂級目錄是壓縮文件的名稱,所有在復制的過程中需要注意目錄的層次關系; 下面我們來走一下實現的整個流程,雖然沒有給出整個源碼,但是如果看完這篇文章的你基本實現起來沒什么大問題了; 為了部署方便我建議大家麻煩點實現一個部署文件的工具,將所有的文件直接打包在里面同時生成服務器端的版本信息文件;  利用這個工具就很方便的實現了對文件進行壓縮、生成HASH值、版本文件、更新地址等信息;  這個XML中保存的是服務當前的版本信息、更新文件的名稱、更新文件的HASH值,為什么需要HASH就是怕更新文件在某些情況下被人調包了,如果所有的客戶端更新后后果很嚴重;所以我們必須帶上HASH值;  工具生成兩個文件,一個是版本文件一個是更新包,服務器的任務已經完成,下面就是具體的客戶端的實現; 為了知道何時需要進行版本更新所以要在客戶端程序目錄中保存一份用來記錄版本信息的文件;  文件中保存著當前本地的版本號、服務器的更新地址、宿主程序的名稱,需要宿主的名稱就能在更新的時候將宿主程序重進程中枚舉出來然后關掉,這樣就不影響我們更新了,當然也可以實現宿主程序不關閉的情況下更新,如果用到某些已經被宿主程序占用的情況會直接影響更新流程,所以以防萬一關了為妙;  這是客戶端版本文件中保存的信息; 我們上面說了,更新分為手動和自動,我們先來說手動更新吧,手動更新就是需要用戶自己去點擊更新按鈕然后開始更新,這個問題我們可以利用進程的參數傳遞解決;  當然在更新程序里面需要有這方面的邏輯判斷;  入口的地方我們進行判斷,更新方式;這里的下載遠程更新包是用WebClient對象,也可以用其他的基于Socket的對象;更新開始之前需要先判斷本地的版本號是否小于遠程版本號,如果小于在進行更新;  因為下載的過程是異步的所以需要用到后臺線程建議大家使用System.ComponentModel.BackgroundWorker這個后臺線程對象,他對Thread進行了很好的封裝;下面來看一下核心的流程代碼: | 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 | `//開始輔助線程操作``????????``private` `void` `Back_thread_DoWork(``object` `sender, DoWorkEventArgs e)``????????``{``????????????``try``????????????``{``????????????????``//實例化下載對象``????????????????``downclient =?``new` `WebClient();``????????????????``downclient.DownloadProgressChanged +=?``new` `DownloadProgressChangedEventHandler(downclient_DownloadProgressChanged);``????????????????``downclient.DownloadFileCompleted +=?``new` `AsyncCompletedEventHandler(downclient_DownloadFileCompleted);``????????????????``//下載遠程更新包down.zip壓縮文件\|放在應用程序目錄下\|相應界面事件``????????????????``downclient.DownloadFileAsync(``new` `Uri(Util.GetUpdateUrl() +?``"down.zip"``), Util.GetDictiory() +?``"\\down.zip"``);``????????????``}``????????????``catch` `(Exception err) { System.Diagnostics.Debug.WriteLine(err); }``????????``}``????????``//在異步下載結束時觸發該事件``????????``void` `downclient_DownloadFileCompleted(``object` `sender, AsyncCompletedEventArgs e)``????????``{``????????????``try``????????????``{` `????????????????``if` `(e.Error !=?``null``)``????????????????``{``????????????????????``eventLog1.WriteEntry(e.Error.ToString());``????????????????????``MessageBox.Show(``"在進行遠程更新時,發生錯誤"``,?``"信息提示"``, MessageBoxButtons.OK, MessageBoxIcon.Error);``????????????????``}``????????????????``else``????????????????``{``????????????????????``Util.KillProcess();``//關閉主進程``????????????????????``//驗證哈希值``????????????????????``if` `(Util.IsHash(Util.GetHash(Util.GetDictiory() +?``"\\down.zip"``), FileWork.GetDownHash()))``????????????????????``{``????????????????????????``//刪除無用壓縮文件``????????????????????????``File.Delete(Util.GetDictiory() +?``"\\down.zip"``);``????????????????????????``//刪除無用版本文件``????????????????????????``File.Delete(Util.GetDictiory() +?``"\\ServerUpdateFiles.xml"``);``????????????????????????``MessageBox.Show(``"遠程服務器更新包已發生變化,無法更新"``,?``"信息提示"``, MessageBoxButtons.OK, MessageBoxIcon.Error);``????????????????????????``eventLog1.WriteEntry(``"遠程服務器中的更新包在制作和下載時間段中數據包發生變化,為了安全期間不給予下載!"``);``????????????????????????``this``.Close();``????????????????????``}``????????????????????``else``????????????????????``{``????????????????????????``//解壓壓縮包文件``????????????????????????``ReduceToUnReduceFile.unZipFile(Util.GetDictiory() +?``"\\down.zip"``, Util.GetDictiory());``????????????????????????``//刪除壓縮包文件``????????????????????????``File.Delete(Util.GetDictiory() +?``"\\down.zip"``);``????????????????????????``//檢查文件夾層次結構``????????????????????????``FileWork.LookFiles(Util.GetDictiory() +?``"\\down"``, Util.GetDictiory());``????????????????????????``//訂閱復制文件事件``????????????????????????``FileWork.CopyFileEvent +=?``new` `FileWork.CopyFileDelegate(FileWork_CopyFileEvent);``????????????????????????``//遞歸復制文件``????????????????????????``FileWork.CopyFiles(Util.GetDictiory() +?``"\\down"``, Util.GetDictiory());``????????????????????????``//刪除臨時文件夾``????????????????????????``FileWork.DeleteFiles(Util.GetDictiory() +?``"\\down"``);``????????????????????????``//如果庫結構更新成功,則才能更新程序的版本號,否則下次繼續更新``????????????????????????``if` `(EventChainReference.GlobalEventChain.OnAutoUpdateDb())``????????????????????????????``//更新本地版本號信息``????????????????????????????``Util.UpdateLocalXml();``????????????????????????``File.Delete(Util.GetDictiory() +?``"\\ServerUpdateFiles.xml"``);` `????????????????????????``MessageBox.Show(``"升級成功!"``,?``"信息提示"``);``????????????????????????``Util.StartProcess();``????????????????????????``isupdate =?``true``;` `????????????????????``}``????????????????``}``????????????``}``????????????``catch` `(Exception err) { eventLog1.WriteEntry(err.ToString()); }``????????????``Application.Exit();``????????``}` | | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 這部分代碼是串聯整個過程的代碼; 自動更新大概就講完了,幾個關鍵的地方都給出了,希望對大家開發自動更新程序有幫助; 分類: [C#](https://www.cnblogs.com/yelanggu/category/872503.html) 最后修改: ? 允許規范轉載 打賞 贊賞作者 支付寶微信 贊 如果覺得我的文章對你有用,請隨意贊賞