C#實現之(自動更新)


做開發的人,尤其是做客戶端(C/S)系統開發的人都會遇到一個頭疼的問題,就是軟件的自動更新;系統發布后怎樣自動的更新程序,在下有幸開發過一個自動更新程序,更新程序與任何宿主程序是完全獨立的;只要在主程序里面啟動更新程序就行了;更新程序也是一個可執行文件,在啟動的時候可以設置是否是自動更新和是否是手動更新,自動更新的意思就是說不需要人工的干預實現從遠程服務器下載更新包,而如果是手動更新就會涉及到用戶點擊程序中的按鈕實現更新;在自動更新與手動更新中可以根據項目的需要進行選擇,有的程序必須要求用戶進行更新才能繼續使用,所以程序自動更新是有必要的;手動更新就是用戶可以隨時更新程序,不需要嚴格的控制版本問題;下面本人就來講一下具體的實現細節,我貼出部分代碼,源碼屬公司財產本人不宜上傳;

自動更新的目的就是將服務器上的DLL文件拷貝到本地執行目錄中,並且覆蓋本地同名的文件;流程很簡單,但是實現起來有幾個地方需要注意:

1.大批量的DLL文件怎么下載到本地來,有多個DLL文件在下載過程中如果網速慢的情況下可能出現丟包、丟文件等情況;本人的實現是將多個文件通過ICSharpCode.SharpZipLib組件進行打包,這樣可以省很多事;(如:動態連接庫文件dll的名稱在傳輸過程中大小寫可能會變化)

2.下載到本地了,怎么覆蓋原有的同名文件;本人的實現是先同名的文件的支持刪除,然后解壓縮;這個過程需要臨時保存刪除的文件,防止操作失敗程序無法啟動,要注意有事務性的原理;

3.如果更新的文件不只是單單的DLL文件可能還有一些無限極的文件夾;本人的實現是如果存在同名的文件夾,直接遞歸的刪除,然后將其解壓縮到目錄中;由於壓縮包解壓后的頂級目錄是壓縮文件的名稱,所有在復制的過程中需要注意目錄的層次關系;

下面我們來走一下實現的整個流程,雖然沒有給出整個源碼,但是如果看完這篇文章的你基本實現起來沒什么大問題了;

為了部署方便我建議大家麻煩點實現一個部署文件的工具,將所有的文件直接打包在里面同時生成服務器端的版本信息文件;

利用這個工具就很方便的實現了對文件進行壓縮、生成HASH值、版本文件、更新地址等信息;

這個XML中保存的是服務當前的版本信息、更新文件的名稱、更新文件的HASH值,為什么需要HASH就是怕更新文件在某些情況下被人調包了,如果所有的客戶端更新后后果很嚴重;所以我們必須帶上HASH值;

工具生成兩個文件,一個是版本文件一個是更新包,服務器的任務已經完成,下面就是具體的客戶端的實現;

為了知道何時需要進行版本更新所以要在客戶端程序目錄中保存一份用來記錄版本信息的文件;

文件中保存着當前本地的版本號、服務器的更新地址、宿主程序的名稱,需要宿主的名稱就能在更新的時候將宿主程序重進程中枚舉出來然后關掉,這樣就不影響我們更新了,當然也可以實現宿主程序不關閉的情況下更新,如果用到某些已經被宿主程序占用的情況會直接影響更新流程,所以以防萬一關了為妙;

這是客戶端版本文件中保存的信息;

我們上面說了,更新分為手動和自動,我們先來說手動更新吧,手動更新就是需要用戶自己去點擊更新按鈕然后開始更新,這個問題我們可以利用進程的參數傳遞解決;

當然在更新程序里面需要有這方面的邏輯判斷;

入口的地方我們進行判斷,更新方式;這里的下載遠程更新包是用WebClient對象,也可以用其他的基於Socket的對象;更新開始之前需要先判斷本地的版本號是否小於遠程版本號,如果小於在進行更新;

因為下載的過程是異步的所以需要用到后台線程建議大家使用System.ComponentModel.BackgroundWorker這個后台線程對象,他對Thread進行了很好的封裝;下面來看一下核心的流程代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//開始輔助線程操作
         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();
         }

這部分代碼是串聯整個過程的代碼;

自動更新大概就講完了,幾個關鍵的地方都給出了,希望對大家開發自動更新程序有幫助;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM