C# WinForm實(shí)現(xiàn)自動(dòng)更新程序的方法詳解
這一篇就著重寫一下客戶端的代碼,客戶端主要實(shí)現(xiàn)的有:啟動(dòng)后檢測本地的xml文件,然后發(fā)送到服務(wù)器獲取需要更新的文件以及版本列表。循環(huán)下載。下載成功后,備份原始文件->復(fù)制到主目錄(若失敗進(jìn)行回滾)->修改本地xml文件,更新完成后打開主程序。
開發(fā)環(huán)境
.NET Core 3.1
開發(fā)工具
Visual Studio 2019
實(shí)現(xiàn)代碼
/// <summary> /// 更新自己 /// </summary> private void CopyRun() { string runPath = Process.GetCurrentProcess().MainModule.FileName; string runName = Path.GetFileName(runPath); if(!runName.StartsWith("_")) { string copyPath = runPath.Replace(runName, "_" + runName); byte[] bytes; using(FileStream fileStream = new FileStream(runPath, FileMode.Open, FileAccess.Read)) { bytes = new byte[fileStream.Length]; fileStream.Read(bytes, 0, bytes.Length); } using(FileStream fileStream = new FileStream(copyPath, FileMode.Create, FileAccess.Write)) { fileStream.Write(bytes); } ProcessStartInfo info = new ProcessStartInfo(copyPath, _runApp); Process.Start(info); Environment.Exit(0); } } /// <summary> /// 更新UI /// </summary> /// <param name="actoin"></param> private void UpdateUI(Action actoin) { if(this.InvokeRequired) { this.BeginInvoke(actoin); } else { actoin(); } } /// <summary> /// 獲取本地更新地址 /// </summary> /// <returns></returns> private string GetUpdateUrl() { XElement xele = XElement.Load(updateXml); string url = xele.Element("url").Value; return url; } /// <summary> /// 獲取本地更新文件 /// </summary> /// <returns></returns> private string GetUpdateFiles() { XDocument xdoc = XDocument.Load(updateXml); var files = from f in xdoc.Root.Element("files").Elements() select new { name = f.Attribute("name").Value, version = f.Attribute("version").Value }; return JsonConvert.SerializeObject(files); } /// <summary> /// 更新完成后修改版本文件 /// </summary> /// <param name="list"></param> private void UpdateXml(List<UpdateModel> list) { XDocument xdoc = XDocument.Load(updateXml); foreach(var model in list) { var ele_files = xdoc.Root.Element("files"); XElement xele = ele_files.Elements().FirstOrDefault(s => s.Attribute("name").Value == model.name); if(xele != null) { xele.SetAttributeValue("version", model.version); } else { XElement addXele = new XElement("file"); addXele.SetAttributeValue("name", model.name); addXele.SetAttributeValue("version", model.version); ele_files.Add(addXele); } } xdoc.Save(updateXml); }
readonly string _runApp; public Form_update(string runApp) { InitializeComponent(); _runApp = runApp; //CopyRun(); } readonly string updateXml = Application.StartupPath + "UpdateList.xml"; private void btn_update_Click(object sender, EventArgs e) { #region 設(shè)置ui btn_update.Enabled = false; label_status.Text = "正在更新"; #endregion #region 初始化路徑 string savePath = Application.StartupPath + "temp_update\\"; string backPath = Application.StartupPath + "temp_back\\"; if(Directory.Exists(savePath)) { Directory.Delete(savePath, true); } Directory.CreateDirectory(savePath); if(Directory.Exists(backPath)) { Directory.Delete(backPath, true); } Directory.CreateDirectory(backPath); #endregion #region 圖標(biāo)旋轉(zhuǎn) int angle = 0; Image img = pictureBox1.BackgroundImage; System.Threading.Timer timer = new System.Threading.Timer(s => { UpdateUI(() => { angle = angle == 360 ? 0 : angle + 10; pictureBox1.BackgroundImage = img.RotateImage(angle); }); }, null, 0, 100); #endregion #region 下載更新 string url = GetUpdateUrl(); bool isSuccess = false; Task.Run(() => { try { //獲取下載列表 HttpResult httpResult = HttpUtil.HttpRequest(new HttpItem(url + "GetUpdateFiles", requestData: GetUpdateFiles())); if(httpResult.Status) { UpdateModel_Out output = JsonConvert.DeserializeObject<UpdateModel_Out>(httpResult.HttpStringData); if(output.updateList.Count == 0) { throw new Exception("當(dāng)前已是最新版本"); } UpdateUI(() => { progressBar1.Maximum = output.updateList.Count + 1; }); //循環(huán)下載文件 for(int i = 0; i < output.updateList.Count; i++) { UpdateModel updateModel = output.updateList[i]; #region 進(jìn)度條 UpdateUI(() => { label_status.Text = $"正在更新第 {i + 1}/{output.updateList.Count} 個(gè)文件,文件名:{updateModel.name}"; progressBar1.Value = i + 1; }); #endregion #region 下載文件 httpResult = HttpUtil.HttpRequest(new HttpItem(url + "DownloadFile", requestData: JsonConvert.SerializeObject(updateModel))); if(httpResult.Status) { using(FileStream fileStream = new FileStream(savePath + updateModel.name, FileMode.Create)) { fileStream.Write(httpResult.HttpByteData, 0, httpResult.HttpByteData.Length); } } else { throw new Exception(updateModel.name + "下載失敗,請重試"); } #endregion Task.Delay(1000).Wait(); } #region 備份 UpdateUI(() => { label_status.Text = "正在備份"; }); try { File.Copy(updateXml, backPath + "UpdateList.xml"); foreach(var file in output.updateList) { if(File.Exists(Application.StartupPath + file.name)) { File.Copy(Application.StartupPath + file.name, backPath + file.name, true); } } } catch { } #endregion #region 完成更新 UpdateUI(() => { label_status.Text = "正在完成更新"; progressBar1.Value = progressBar1.Maximum; }); string[] files = Directory.GetFiles(savePath); try { #region 更新成功 foreach(string file in files) { File.Copy(file, Application.StartupPath + Path.GetFileName(file), true); } Directory.Delete(savePath, true); #region 保存最新版本 UpdateXml(output.updateList); isSuccess = true; UpdateUI(() => { label_status.Text = "更新完成"; }); #endregion #endregion } catch { #region 失敗回滾 UpdateUI(() => { label_status.Text = "更新失敗,正在回滾"; }); string[] files_back = Directory.GetFiles(backPath); foreach(string file in files_back) { File.Copy(file, Application.StartupPath + Path.GetFileName(file), true); } File.Copy(backPath + "UpdateList.xml", updateXml, true); UpdateUI(() => { label_status.Text = "回滾完成"; }); return; #endregion } #endregion } else { throw new Exception("獲取更新列表失敗"); } } catch(Exception ex) { UpdateUI(() => { label_status.Text = "更新失敗!" + ex.Message; btn_update.Enabled = true; }); } finally { UpdateUI(() => { timer.Change(-1, -1); pictureBox1.BackgroundImage = img; if(isSuccess) { if(File.Exists(_runApp)) { if(MessageBox.Show("更新完成,是否打開程序", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { Process.Start(_runApp); } Environment.Exit(0); } } }); } }); #endregion } private void btn_close_Click(object sender, EventArgs e) { Close(); } private void panel1_Paint(object sender, PaintEventArgs e) { Pen pen = new Pen(Color.LightGray); e.Graphics.DrawLine(pen, new Point(36, 36), new Point(panel1.Width - 36, 36)); }
實(shí)現(xiàn)效果
代碼解析
主要注釋已經(jīng)在代碼中標(biāo)注了,由于基本都是在線程中進(jìn)行操作的,所以在更新UI的時(shí)候封裝了UpdateUI方法,然后就是加了一個(gè)定時(shí)器實(shí)現(xiàn)圖標(biāo)的旋轉(zhuǎn)。關(guān)于CopyRun(更新自己)方法,就是用來更新自動(dòng)更新程序本身的,即運(yùn)行之前復(fù)制一份本身,然后啟動(dòng)復(fù)制的程序,否則本身正在運(yùn)行的時(shí)候是不能更新自己的。
以上就是C# WinForm實(shí)現(xiàn)自動(dòng)更新程序的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于C# WinForm自動(dòng)更新程序的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Unity實(shí)現(xiàn)3D循環(huán)滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)3D循環(huán)滾動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02C#實(shí)現(xiàn)的ZPL條碼打印類完整實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)的ZPL條碼打印類,結(jié)合實(shí)例形式詳細(xì)分析了C#實(shí)現(xiàn)條碼打印的原理與使用方法,代碼注釋中備有詳盡的說明,便于理解使用,需要的朋友可以參考下2016-06-06c#中將uint值轉(zhuǎn)換成int的實(shí)例方法
在本文里小編給大家整理的是關(guān)于c#中將uint值轉(zhuǎn)換成int的實(shí)例方法,需要的朋友們學(xué)習(xí)參考下。2019-08-08C#控制臺(tái)輸出進(jìn)度和百分比的實(shí)例代碼
C#控制臺(tái)輸出進(jìn)度和百分比的實(shí)例代碼,需要的朋友可以參考一下2013-03-03使用Spire.Barcode程序庫生成二維碼的實(shí)例解析
這篇文章主要介紹了使用Spire.Barcode程序庫生成二維碼的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-12-12C#中利用斷點(diǎn)操作調(diào)試程序的步驟詳解
所謂斷點(diǎn)調(diào)試就是檢測執(zhí)行路徑和數(shù)據(jù)是否正確,中斷游戲運(yùn)行在線調(diào)試,下面這篇文章主要給大家介紹了關(guān)于C#中利用斷點(diǎn)操作調(diào)試程序的相關(guān)資料,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12C#實(shí)現(xiàn)可緩存網(wǎng)頁到本地的反向代理工具實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)可緩存網(wǎng)頁到本地的反向代理工具,實(shí)例分析了C#實(shí)現(xiàn)反向代理的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04