c# 如何實(shí)現(xiàn)自動(dòng)更新程序
主要功能介紹
實(shí)現(xiàn)文件的自動(dòng)更新。主要功能:
- 支持整包完全更新,即客戶端只需輸入一個(gè)服務(wù)器地址,即可下載所有文件。
- 支持增量更新,即只更新指定的某幾個(gè)文件。
- 支持自動(dòng)更新程序的更新
更新界面如圖:
客戶端
main方法入口
/// <summary> /// 應(yīng)用程序的主入口點(diǎn)。 /// </summary> [STAThread] static void Main() { //在主程序中 更新替換自動(dòng)升級(jí)程序 //ReplaceAutoUpgrade(); bool isEnterMain = false; try { //設(shè)置默認(rèn)更新地址,如果不設(shè)置,后面會(huì)從配置文件,或界面上進(jìn)行設(shè)置 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) { //沒有待更新,并且本地版本信息文件不為空,則直接啟動(dòng)主程序 bool isSucced = UpgradeHelper.StartRunMain(UpgradeHelper.Instance.Local_UpgradeModel.RunMain); if (isSucced) { Application.Exit(); } else { //清理版本信息 以便重新檢測(cè)版本 UpgradeHelper.Instance.ClearUpgradeModel(); isEnterMain = true; } } else { isEnterMain = true; } } catch (Exception ex) { isEnterMain = true; MessageBox.Show("運(yùn)行更新程序異常:\n" + ex.Message, "錯(cuò)誤提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } if (isEnterMain) { //進(jìn)入更新主界面 Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new FrmUpdate()); } }
主窗體代碼
public partial class FrmUpdate: Form { /// <summary> /// 構(gòu)造函數(shù) /// </summary> /// <param name="tempPath"></param> /// <param name="updateFiles"></param> public FrmUpdate() { InitializeComponent(); } /// <summary> /// 窗體加載事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void FrmUpdate_Load(object sender, EventArgs e) { try { //加載服務(wù)器地址 txtHostUrl.Text = UpgradeHelper.Instance.UpgradeUrl; BeginUpgrade(); } catch(Exception ex) { Output("初始化異常:" + ex.Message); } } /// <summary> /// 手動(dòng)更新 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void butBegin_Click(object sender, EventArgs e) { try { if(string.IsNullOrWhiteSpace(txtHostUrl.Text)) { Output("請(qǐng)先輸入服務(wù)器地址!"); return; } UpgradeHelper.Instance.UpgradeUrl = txtHostUrl.Text.Trim(); //清理版本信息 以便重新檢測(cè)版本 UpgradeHelper.Instance.ClearUpgradeModel(); BeginUpgrade(); } catch(Exception ex) { Output("更新異常:" + ex.Message); } } private void BeginUpgrade() { try { if(string.IsNullOrWhiteSpace(UpgradeHelper.Instance.UpgradeUrl)) { return; } if(!(UpgradeHelper.Instance.UpgradeUrl.StartsWith("http://") || UpgradeHelper.Instance.UpgradeUrl.StartsWith("https://"))) { Output("錯(cuò)誤的服務(wù)器地址,地址必須以http://或者h(yuǎn)ttps://開頭"); return; } //判斷是否有更新 if(UpgradeHelper.Instance.WillUpgrades.Count > 0 && UpgradeHelper.Instance.Server_UpgradeModel != null) { SetWinControl(false); //殺死主進(jìn)程 UpgradeHelper.KillProcess(UpgradeHelper.Instance.Server_UpgradeModel.RunMain); RunUpgrade(); //啟動(dòng)更新 } } catch(Exception ex) { Output("更新異常:" + ex.Message); } } /// <summary> /// 啟動(dòng)更新 /// </summary> private void RunUpgrade() { //啟動(dòng)更新 SetCaption(string.Format("共需更新文件{0}個(gè),已更新0個(gè)。正在更新下列文件:", UpgradeHelper.Instance.WillUpgrades.Count)); Task.Factory.StartNew(() => { string curFile = ""; try { int idx = 0; foreach(KeyValuePair < string, string > item in UpgradeHelper.Instance.WillUpgrades) { curFile = item.Key; string filePath = string.Format("{0}\\{1}", Application.StartupPath, item.Key); if(item.Key.IndexOf(UpgradeHelper.Instance.Server_UpgradeModel.AutoUpgrade) >= 0) { //如果當(dāng)前文件為更新主程序 filePath = string.Format("{0}\\AutoUpgradeTemp\\{1}", Application.StartupPath, item.Key); } string directory = Path.GetDirectoryName(filePath); if(!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } MyWebResquest.DownloadFile(UpgradeHelper.Instance.UpgradeUrl, item.Key, filePath); idx++; SetCaption(string.Format("共需更新文件{0}個(gè),已更新{1}個(gè)。更新文件列表:", UpgradeHelper.Instance.WillUpgrades.Count, idx)); Output(string.Format("更新文件{0}完成", curFile)); } //保存版本文件 File.WriteAllText(UpgradeHelper.Instance.Local_UpgradeXmlPath, UpgradeHelper.Instance.Server_UpgradeXml); SetCaption(string.Format("更新完成,共更新文件{0}個(gè)", UpgradeHelper.Instance.WillUpgrades.Count)); Output(string.Format("更新完成,共更新文件{0}個(gè)", UpgradeHelper.Instance.WillUpgrades.Count)); //下載完成后處理 UpgradeHelper.StartRunMain(UpgradeHelper.Instance.Server_UpgradeModel.RunMain); //退出當(dāng)前程序 ExitCurrent(); } catch(Exception ex) { Output(string.Format("更新文件{0}異常:{1}", curFile, ex.Message)); SetWinControl(true); } }); } /// <summary> /// 設(shè)置界面控件是否可用 /// </summary> /// <param name="enabled"></param> private void SetWinControl(bool enabled) { if(this.InvokeRequired) { Action < bool > d = new Action < bool > (SetWinControl); this.Invoke(d, enabled); } else { txtHostUrl.Enabled = enabled; butBegin.Enabled = enabled; } } /// <summary> /// 退出當(dāng)前程序 /// </summary> private void ExitCurrent() { if(this.InvokeRequired) { Action d = new Action(ExitCurrent); this.Invoke(d); } else { Application.Exit(); } }# region 日志輸出 /// <summary> /// 設(shè)置跟蹤狀態(tài) /// </summary> /// <param name="caption"></param> private void SetCaption(string caption) { if(this.lblCaption.InvokeRequired) { Action < string > d = new Action < string > (SetCaption); this.Invoke(d, caption); } else { this.lblCaption.Text = caption; } } /// <summary> /// 設(shè)置跟蹤狀態(tài) /// </summary> /// <param name="caption"></param> private void Output(string log) { if(this.txtLog.InvokeRequired) { Action < string > d = new Action < string > (Output); this.Invoke(d, log); } else { txtLog.AppendText(string.Format("{0}:{1}\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), log)); txtLog.ScrollToCaret(); } } private void ClearOutput() { if(this.txtLog.InvokeRequired) { Action d = new Action(ClearOutput); this.Invoke(d); } else { txtLog.Text = ""; } }# endregion private void FrmUpdate_FormClosing(object sender, FormClosingEventArgs e) { if(e.CloseReason == CloseReason.UserClosing) { if(MessageBox.Show("升級(jí)未完成,退出后將導(dǎo)致軟件無(wú)法正常使用,你確定要退出嗎?", "退出提示", MessageBoxButtons.YesNo) != System.Windows.Forms.DialogResult.Yes) { //取消"關(guān)閉窗口"事件 e.Cancel = true; } } } }
更新幫助類
/// <summary> /// 更新幫助類 /// </summary> public class UpgradeHelper { /// <summary> /// 默認(rèn)服務(wù)器地址 /// 在配置文件中未找到地址時(shí),使用此地址進(jìn)行更新 /// </summary> public string DefaultUrl { get; set; } public string _upgradeUrl; /// <summary> /// 獲取或設(shè)置服務(wù)器地址 /// </summary> public string UpgradeUrl { get { if(string.IsNullOrWhiteSpace(_upgradeUrl)) { return DefaultUrl; } return _upgradeUrl; } set { _upgradeUrl = value; } } /// <summary> /// 本地配置文件路徑 /// </summary> public string Local_UpgradeXmlPath = Path.Combine(Application.StartupPath, "UpgradeList.xml"); private UpgradeModel _local_UpgradeModel; /// <summary> /// 本地版本信息 /// </summary> public UpgradeModel Local_UpgradeModel { get { try { if(_local_UpgradeModel == null) { if(File.Exists(Local_UpgradeXmlPath)) { _local_UpgradeModel = new UpgradeModel(); _local_UpgradeModel.LoadUpgrade(File.ReadAllText(Local_UpgradeXmlPath)); } } return _local_UpgradeModel; } catch(Exception ex) { throw new Exception(string.Format("獲取本地版本文件UpgradeList.xml異常:{0}", ex.Message)); } } } private UpgradeModel _server_UpgradeModel; /// <summary> /// 服務(wù)器版本信息 /// </summary> public UpgradeModel Server_UpgradeModel { get { try { if(_server_UpgradeModel == null && !string.IsNullOrWhiteSpace(UpgradeUrl)) { string resXml = MyWebResquest.GetUpgradeList(UpgradeUrl); if(!string.IsNullOrWhiteSpace(resXml)) { _server_UpgradeModel = new UpgradeModel(); _server_UpgradeModel.LoadUpgrade(resXml); _server_UpgradeXml = resXml; } } return _server_UpgradeModel; } catch(Exception ex) { throw new Exception(string.Format("獲取服務(wù)端版本文件UpgradeList.xml異常:{0}", ex.Message)); } } } private string _server_UpgradeXml; /// <summary> /// 服務(wù)端版本配置xml /// </summary> public string Server_UpgradeXml { get { return _server_UpgradeXml; } } private Dictionary < string, string > _willUpgrades; /// <summary> /// 待更新文件列表,如果為0,則表示不需要更新 /// </summary> public Dictionary < string, string > WillUpgrades { get { if(_willUpgrades == null) { _willUpgrades = new Dictionary < string, string > (); //如果服務(wù)器端未獲取到版本信息 則不更新 if(Server_UpgradeModel != null) { if(Local_UpgradeModel == null) //本地版本信息為空 全部更新 { _willUpgrades = Server_UpgradeModel.DictFiles; } else { //對(duì)比需要更新的文件 foreach(var item in Server_UpgradeModel.DictFiles) { //如果找到 if(Local_UpgradeModel.DictFiles.ContainsKey(item.Key)) { //如果版本不匹配 if(Local_UpgradeModel.DictFiles[item.Key] != item.Value) { _willUpgrades.Add(item.Key, item.Value); } } else { //沒有找到 _willUpgrades.Add(item.Key, item.Value); } } } } } return _willUpgrades; } } /// <summary> /// 清空版本信息 /// </summary> public void ClearUpgradeModel() { if(File.Exists(Local_UpgradeXmlPath)) { try { string xmlStr = File.ReadAllText(Local_UpgradeXmlPath); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xmlStr); XmlNode node = xmlDoc.SelectSingleNode("Upgrade/Files"); if(node != null && node.ChildNodes.Count > 0) { node.RemoveAll(); } File.WriteAllText(UpgradeHelper.Instance.Local_UpgradeXmlPath, xmlDoc.InnerXml); } catch(Exception) {} } _local_UpgradeModel = null; _server_UpgradeModel = null; _willUpgrades = null; }# region 單例對(duì)象 private static UpgradeHelper _instance; /// <summary> /// 單例對(duì)象 /// </summary> public static UpgradeHelper Instance { get { if(_instance == null) { _instance = new UpgradeHelper(); //初始化本地配置文件,以及服務(wù)器地址 if(_instance.Local_UpgradeModel != null) { _instance.UpgradeUrl = _instance.Local_UpgradeModel.UpgradeUrl; } } return _instance; } }# endregion# region 靜態(tài)方法 /// <summary> /// 啟動(dòng)主程序 /// </summary> /// <param name="fileName"></param> public static bool StartRunMain(string fileName) { string fullPath = fileName; try { Process process = GetProcess(fileName); if(process != null) //以及存在運(yùn)行中的主進(jìn)程 { return true; } fullPath = string.Format("{0}\\{1}", Application.StartupPath, fileName); ProcessStartInfo main = new ProcessStartInfo(fullPath); Process.Start(fullPath); return true; } catch(Exception ex) { MessageBox.Show(string.Format("主程序{0}調(diào)用失敗:\n{1}", fullPath, ex.Message), "錯(cuò)誤提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } return false; } /// <summary> /// 殺死進(jìn)程 /// </summary> /// <param name="process"></param> public static void KillProcess(string processName) { if(string.IsNullOrWhiteSpace(processName)) return; processName = processName.ToLower(); processName = processName.Replace(".exe", ""); //殺死主進(jìn)程 Process[] processes = Process.GetProcesses(); foreach(Process process in processes) { if(!string.IsNullOrWhiteSpace(process.ProcessName)) { if(process.ProcessName.ToLower() == processName) { process.Kill(); } } } } /// <summary> /// 獲取進(jìn)程 /// </summary> /// <param name="pName"></param> /// <returns></returns> public static Process GetProcess(string pName) { if(string.IsNullOrWhiteSpace(pName)) return null; pName = pName.ToLower(); pName = pName.Replace(".exe", ""); //殺死主進(jìn)程 Process[] processes = Process.GetProcesses(); foreach(Process process in processes) { if(!string.IsNullOrWhiteSpace(process.ProcessName)) { if(process.ProcessName.ToLower() == pName) { return process; } } } return null; }# endregion }
版本xml文件解析
public class UpgradeModel { /// <summary> /// 初始化對(duì)象 /// </summary> /// <param name="xml"></param> public void LoadUpgrade(string xml) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xml); //讀取UpgradeUrl XmlNode node = xmlDoc.SelectSingleNode("http://UpgradeUrl"); if(node != null) { this.UpgradeUrl = node.InnerText; } //讀取RunMain node = xmlDoc.SelectSingleNode("http://RunMain"); if(node != null) { this.RunMain = node.InnerText; } //讀取RunMain node = xmlDoc.SelectSingleNode("http://AutoUpgrade"); if(node != null) { this.AutoUpgrade = node.InnerText; } //讀取Files node = xmlDoc.SelectSingleNode("Upgrade/Files"); this.DictFiles = new Dictionary < string, string > (); if(node != null && node.ChildNodes.Count > 0) { foreach(XmlNode item in node.ChildNodes) { if(item.Name != "#comment") { string name = GetNodeAttrVal(item, "Name"); string version = GetNodeAttrVal(item, "Version"); if(!this.DictFiles.ContainsKey(name)) { this.DictFiles.Add(name, version); } } } } } private string GetNodeAttrVal(XmlNode node, string attr) { if(node != null && node.Attributes != null && node.Attributes[attr] != null) { string val = node.Attributes[attr].Value; if(!string.IsNullOrWhiteSpace(val)) { return val.Trim(); } return val; } return string.Empty; } /// <summary> /// 服務(wù)器地址 /// </summary> public string UpgradeUrl { get; set; } /// <summary> /// 更新完成后運(yùn)行的主程序名稱 /// </summary> public string RunMain { get; set; } /// <summary> /// 更新程序名稱 /// </summary> public string AutoUpgrade { get; set; } /// <summary> /// 文件列表 /// string 文件名 /// string 版本號(hào) /// </summary> public Dictionary < string, string > DictFiles { get; set; } }
服務(wù)端
服務(wù)端主Xml版本文件,包含所有的項(xiàng)目文件,客戶端根據(jù)每個(gè)文件的版本號(hào)進(jìn)行判斷是否需要更新。如果需只更新某幾個(gè)文件,則將對(duì)應(yīng)文件的版本號(hào)更改只更高的版本號(hào)即可
版本xml文件
<?xml version="1.0" encoding="utf-8" ?> <Upgrade> <!--服務(wù)器地址--> <UpgradeUrl>http://localhost:17580</UpgradeUrl> <!--更新完成后運(yùn)行的主程序名稱--> <RunMain>ClientMain.exe</RunMain> <!--更新程序名稱--> <AutoUpgrade>AutoUpgrade.exe</AutoUpgrade> <Files> <!--更新文件列表,以Version為標(biāo)志,當(dāng)Version改變時(shí),客戶端啟動(dòng)會(huì)自動(dòng)更新。子路徑格式:\image\index.jpg--> <File Version="01" Name="\image\index.jpg" /> <File Version="01" Name="ClientMain.exe" /> <File Version="01" Name="AutoUpgrade.exe" /> </Files> </Upgrade>
服務(wù)端主要提供連個(gè)可以通過Http的get或post訪問的路徑。一個(gè)用于獲取版本Xml文件內(nèi)容,一個(gè)用于下載指定文件的路徑。以下代碼示例通過asp.net mvc進(jìn)行實(shí)現(xiàn)。大家可以根據(jù)自己技術(shù)方式參照實(shí)現(xiàn)。
自動(dòng)升級(jí)服務(wù)Controller
/// <summary> /// 自動(dòng)升級(jí)服務(wù) /// </summary> public class UpgradeController: Controller { // // GET: /Upgrade/ /// <summary> /// 獲取更新文件列表 /// </summary> /// <returns></returns> public object UpgradeList() { string cacheKey = "Upgrade_UpgradeList.xml"; string resStr = CommonLibrary.CacheClass.GetCache < string > (cacheKey); if(string.IsNullOrWhiteSpace(resStr)) { string fileName = Server.MapPath(@"~\App_Data\UpgradeList.xml"); if(System.IO.File.Exists(fileName)) { resStr = System.IO.File.ReadAllText(fileName); CommonLibrary.CacheClass.SetCacheMins(cacheKey, resStr, 1); } } return resStr; } /// <summary> /// 生成更新文件 /// </summary> /// <returns></returns> public object Create() { UpgradeFileManager.CreateFiles(Server.MapPath("/App_Data")); return "ok"; } /// <summary> /// 下載文件 /// </summary> /// <param name="fileName"></param> /// <returns></returns> public object DownloadFile() { string fileName = PageRequest.GetString("fileName"); fileName = Server.MapPath(string.Format(@"~\App_Data\{0}", fileName)); return File(fileName, "application/octet-stream"); } /// <summary> /// 異常處理 /// </summary> /// <param name="filterContext"></param> protected override void OnException(ExceptionContext filterContext) { filterContext.HttpContext.Response.StatusCode = 400; filterContext.Result = Content(filterContext.Exception.GetBaseException().Message); filterContext.ExceptionHandled = true; } }
版本文件自動(dòng)生成幫助類
/// <summary> /// 此類主要作用,對(duì)于項(xiàng)目文件非常多,自己手動(dòng)編輯很麻煩,可以采用此方法,指定目錄自動(dòng)生成初始化的版本文件 /// </summary> public class UpgradeFileManager { /// <summary> /// 創(chuàng)建版本文件 /// </summary> /// <param name="path"></param> public static void CreateFiles(string path) { List < string > dirList = new List < string > (); GetAllDirt(path, dirList); //獲取所有目錄 dirList.Add(path); System.Text.StringBuilder xml = new System.Text.StringBuilder(); xml.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>"); xml.AppendLine(" <Files>"); foreach(var diry in dirList) { string[] files = Directory.GetFiles(diry); foreach(string filePath in files) { FileInfo info = new FileInfo(filePath); string name = filePath.Replace(path, ""); if(info.Directory.FullName == path) { name = name.Remove(0, 1); } xml.AppendLine(string.Format(" <File Version=\"1\" Name=\"{0}\" />", name)); } } xml.AppendLine("</Files>"); using(StreamWriter sw = new StreamWriter(Path.Combine(path, "UpgradeList_Temp.xml"))) { sw.Write(xml); sw.Close(); } } /// <summary> /// 獲取所有子目錄 /// </summary> /// <param name="curDir"></param> /// <param name="list"></param> private static void GetAllDirt(string curDir, List < string > list) { string[] dirs = Directory.GetDirectories(curDir); if(dirs.Length > 0) { foreach(string item in dirs) { list.Add(item); GetAllDirt(item, list); } } } }
結(jié)語(yǔ)
源代碼托管于GitHub,供大伙學(xué)習(xí)參考,項(xiàng)目地址:https://github.com/keguoquan/AutoUpgrade。感興趣或覺得不錯(cuò)的望賞個(gè)star,不勝感激!
若能順手點(diǎn)個(gè)贊,更加感謝!
以上就是用c# 自動(dòng)更新程序的詳細(xì)內(nèi)容,更多關(guān)于c# 自動(dòng)更新程序的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- c#基于winform制作音樂播放器
- C#飛機(jī)打字游戲的代碼示例(winform版)
- c#調(diào)用c語(yǔ)言dll需要注意的地方
- C#調(diào)用C類型dll入?yún)閟truct的問題詳解
- C# 使用SHA1算法對(duì)密碼進(jìn)行加密
- C# TreeView從數(shù)據(jù)庫(kù)綁定數(shù)據(jù)的示例
- 使用 BenchmarkDotNet 對(duì) C# 代碼進(jìn)行基準(zhǔn)測(cè)試
- c# 如何對(duì)網(wǎng)絡(luò)信息進(jìn)行相關(guān)設(shè)置(ip,dns,網(wǎng)關(guān)等)
- c# 如何自己實(shí)現(xiàn)一個(gè)ORM框架
- c# HttpClient設(shè)置超時(shí)的步驟
- c# 如何更簡(jiǎn)單的使用Polly
相關(guān)文章
C#獲取鼠標(biāo)在listview右鍵點(diǎn)擊單元格的內(nèi)容方法
下面小編就為大家?guī)硪黄狢#獲取鼠標(biāo)在listview右鍵點(diǎn)擊單元格的內(nèi)容方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01c#遠(yuǎn)程html數(shù)據(jù)抓取實(shí)例分享
這篇文章主要介紹了c#遠(yuǎn)程html數(shù)據(jù)抓取的方法,大家參考使用吧2013-12-12基于WPF繪制一個(gè)點(diǎn)贊大拇指動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了WPF實(shí)現(xiàn)繪制一個(gè)點(diǎn)贊大拇指動(dòng)畫,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,感興趣的小伙伴可以了解一下2023-02-02c#創(chuàng)建windows服務(wù)入門教程實(shí)例
windows服務(wù)是windows系統(tǒng)中一類特殊的應(yīng)用程序,一般情況下它們只會(huì)在后臺(tái)運(yùn)行,不會(huì)影響前臺(tái)操作,非常適合做一些不需要用戶參與的而又需要長(zhǎng)時(shí)間執(zhí)行的任務(wù)2014-04-04C#環(huán)形隊(duì)列的實(shí)現(xiàn)方法詳解
這篇文章先是簡(jiǎn)單的給大家介紹了什么是環(huán)形隊(duì)列和環(huán)形隊(duì)列的優(yōu)點(diǎn),然后通過實(shí)例代碼給大家介紹C#如何實(shí)現(xiàn)環(huán)形隊(duì)列,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-09-09