標籤:源碼 delete sage mode 方便 服務 伺服器 ons plain
做開發的人,尤其是做用戶端(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#實現之(自動更新)