用C# 實現C/S模式下軟體自動線上升級

DaChu發表於2008-08-11
摘 要:

 本文針對目前C/S模式下編寫的應用程式可維護性差的特點,提出了一套自動線上升級的解決方案,分析了線上升級的困難及實現原理,並給出了實現升級的部分程式碼,具有實際參考價值和現實意義。本文程式程式碼均在.Net Framework 1.1 和Windows2000下測試透過。

 關鍵詞:C#;線上升級;自動升級;下載;XML文件

[@more@]1 前言

 長期以來,廣大程式設計師為到底是使用Client/Server,還是使用Browser/Server結構爭論不休,在這些爭論當中,C/S結構的程式可維護性差,佈置困難,升級不方便,維護成本高就是一個相當重要的因素。有很多企業使用者就是因為這個原因而放棄使用C/S。然而當一個應用必須要使用C/S結構才能很好的實現其功能的時候,我們該如何解決客戶端的部署與自動升級問題?部署很簡單,只要點選安裝程式即可,難的在於每當有新版本釋出時,能夠實現自動升級[3]。現在好了,我們的目標很簡單,我們希望開發一個與具體應用無關的能夠複用的自動升級系統。下面我為大家提供了一套可複用的用C#編寫的自動升級系統。

2 實現軟體的自動升級存在的困難

 第一,為了查詢遠端伺服器上的更新,應用程式必須有查詢網路的途徑,這需要網路程式設計、簡單的應用程式與伺服器通訊的協議。

 第二是下載。下載看起來不需要考慮聯網的問題,但要考慮下載使用者請求的檔案,以及在沒有使用者同意時下載大檔案。友好的自動更新應用程式將使用剩餘的頻寬下載更新。這聽起來簡單,但卻是一個技術難題,幸運的是已經有了解決方法。

 第三個考慮因素是使用新版應用程式更換原應用程式的過程。這個問題比較有趣,因為它要求程式碼執行時將自己從系統刪除,有多種辦法可以實現該功能[5],本文程式主要透過比較新舊版本的日期號來實現替換新版本應用程式的功能。

3 實現軟體自動線上升級的原理

 寫兩個程式,一個是主程式;一個是升級程式;所有升級任務都由升級程式完成。

 1.啟動升級程式,升級程式連線到網站,下載新的主程式(當然還包括支援的庫檔案、XML配置文件等)到臨時資料夾;

 2.升級程式獲取伺服器端XML配置檔案中新版本程式的更新日期或版本號或檔案大小;

 3.升級程式獲取原有客戶端應用程式的最近一次更新日期或版本號或檔案大小,兩者進行比較;如果發現升級程式的日期大於原有程式的最新日期,則提示使用者是否升級;或者是採用將現有版本與最新版本作比較,發現最新的則提示使用者是否升級;也有人用其它屬性如檔案大小進行比較,發現升級程式的檔案大小大於舊版本的程式的大小則提示使用者升級。本文主要採用比較新舊版本更新日期號來提示使用者升級。

 4.如果使用者選擇升級,則獲取下載檔案列表,開始進行批次下載文件;

 5.升級程式檢測舊的主程式是否活動,若活動則關閉舊的主程式;

 6.刪除舊的主程式,複製臨時資料夾中的檔案到相應的位置;

 7.檢查主程式的狀態,若狀態為活動的,則啟動新的主程式;

 8.關閉升級程式,升級完成[4]。

4 用C#實現線上升級的關鍵步驟
這裡我主要使用日期資訊來檢測是否需要下載升級版本。
4.1 準備一個XML配置檔案
名稱為AutoUpdater.xml,作用是作為一個升級用的模板,顯示需要升級的資訊。

//xml版本號

http://192.168.198.113/vbroker/log/"/>//升級檔案所在伺服器端的網址

//升級檔案的更新日期
//升級檔案的版本號

//升級檔案列表
//共有三個檔案需升級




//允許重新啟動應用程式
//啟動的應用程式名





 從以上XML文件中可以得知升級文件所在伺服器端的地址、升級文件的更新日期、需要升級的檔案列表,其中共有三個檔案需升級:aa.txt、VB40.rar、VB4-1.CAB。以及是否允許重新啟動應用程式和重新啟動的應用程式名。
4.2 獲取客戶端應用程式及伺服器端升級程式的最近一次更新日期
透過GetTheLastUpdateTime()函式來實現。



private string GetTheLastUpdateTime(string Dir)
{
string LastUpdateTime = "";
string AutoUpdaterFileName = Dir + @"AutoUpdater.xml";
if(!File.Exists(AutoUpdaterFileName))
return LastUpdateTime;
//開啟xml檔案
FileStream myFile = new FileStream(AutoUpdaterFileName,FileMode.Open);
//xml檔案閱讀器
XmlTextReader xml = new XmlTextReader(myFile);
while(xml.Read())
{
if(xml.Name == "UpdateTime")
{
//獲取升級文件的最後一次更新日期
LastUpdateTime = xml.GetAttribute("Date");
break;
}
}
xml.Close();
myFile.Close();
return LastUpdateTime;
}



 透過XmlTextReader開啟XML文件,讀取更新時間從而獲取Date對應的值,即伺服器端升級檔案的最近一次更新時間。

函式呼叫實現:
//獲取客戶端指定路徑下的應用程式最近一次更新時間
string thePreUpdateDate = GetTheLastUpdateTime(Application.StartupPath);
Application.StartupPath指客戶端應用程式所在的路徑。

//獲得從伺服器端已下載文件的最近一次更新日期
string theLastsUpdateDate = GetTheLastUpdateTime(theFolder.FullName);
theFolder.FullName指在升級文件下載到客戶機上的臨時資料夾所在的路徑。

4.3 比較日期
客戶端應用程式最近一次更新日期與伺服器端升級程式的最近一次更新日期進行比較。

//獲得已下載文件的最近一次更新日期
string theLastsUpdateDate = GetTheLastUpdateTime(theFolder.FullName);
if(thePreUpdateDate != "")
{
//如果客戶端將升級的應用程式的更新日期大於伺服器端升級的應用程式的更新日期
if(Convert.ToDateTime(thePreUpdateDate)>=Convert.ToDateTime(theLastsUpdateDate))
{
MessageBox.Show("當前軟體已經是最新的,無需更新!","系統提示",MessageBoxButtons.OK,MessageBoxIcon.Information);
this.Close();
}
}
this.labDownFile.Text = "下載更新檔案";
this.labFileName.Refresh();
this.btnCancel.Enabled = true;
this.progressBar.Position = 0;
this.progressBarTotal.Position = 0;
this.progressBarTotal.Refresh();
this.progressBar.Refresh();

//透過動態陣列獲取下載檔案的列表
ArrayList List = GetDownFileList(GetTheUpdateURL(),theFolder.FullName);
string[] urls = new string[List.Count];
List.CopyTo(urls, 0);



將客戶端升級的應用程式的日期與伺服器端下載的應用程式日期進行比較,如果前者大於後者,則不更新;如果前者小於後者,則透過動態陣列獲取下載檔案的列表,開始下載檔案。
 透過BatchDownload()函式來實現。升級程式檢測舊的主程式是否活動,若活動則關閉舊的主程式;刪除舊的主程式,複製臨時資料夾中的檔案到相應的位置;檢查主程式的狀態,若狀態為活動的,則啟動新的主程式。



private void BatchDownload(object data)
{
this.Invoke(this.activeStateChanger, new object[]{true, false});
try
{
DownloadInstructions instructions = (DownloadInstructions) data;
//批次下載
using(BatchDownloader bDL = new BatchDownloader())
{
bDL.CurrentProgressChanged += new DownloadProgressHandler(this.SingleProgressChanged);
bDL.StateChanged += new DownloadProgressHandler(this.StateChanged);
bDL.FileChanged += new DownloadProgressHandler(bDL_FileChanged);
bDL.TotalProgressChanged += new DownloadProgressHandler(bDL_TotalProgressChanged);
bDL.Download(instructions.URLs, instructions.Destination, (ManualResetEvent) this.cancelEvent);
}
}
catch(Exception ex)
{
ShowErrorMessage(ex);
}
this.Invoke(this.activeStateChanger, new object[]{false, false});
this.labFileName.Text = "";
//更新程式
if(this._Update)
{
//關閉原有的應用程式
this.labDownFile.Text = "正在關閉程式....";
System.Diagnostics.Process[]proc=System.Diagnostics.Process.GetProcessesByName("TIMS");
//關閉原有應用程式的所有程式
foreach(System.Diagnostics.Process pro in proc)
{
pro.Kill();
}
DirectoryInfo theFolder=new DirectoryInfo(Path.GetTempPath()+"JurassicUpdate");
if(theFolder.Exists)
{
foreach(FileInfo theFile in theFolder.GetFiles())
{
//如果臨時資料夾下存在與應用程式所在目錄下的檔案同名的檔案,則刪除應用程式目錄下的檔案
if(File.Exists(Application.StartupPath + "+Path.GetFileName(theFile.FullName)))
File.Delete(Application.StartupPath + ""+Path.GetFileName(theFile.FullName));
//將臨時資料夾的檔案移到應用程式所在的目錄下
File.Move(theFile.FullName,Application.StartupPath + "+Path.GetFileName(theFile.FullName));
}
}
//啟動安裝程式
this.labDownFile.Text = "正在啟動程式....";
System.Diagnostics.Process.Start(Application.StartupPath + "" + "TIMS.exe");
this.Close();
}
}

 這段程式是實現線上升級的關鍵程式碼,步驟有點複雜:首先用Invoke方法同步呼叫狀態改變程式,然後呼叫動態連結庫中批次下載(BatchDownloader.cs)類啟動批次下載,接著判斷原有的主應用程式有沒有關閉,如果沒關閉,則用Process.Kill()來關閉主程式。接下來,判斷臨時資料夾下(即下載升級程式所在的目錄)是否存在與應用程式所在目錄下的檔案同名的檔案,如果存在同名檔案,則刪除應用程式目錄下的檔案,然後將臨時資料夾的檔案移到應用程式所在的目錄下。最後重新啟動主應用程式。這樣更新就完成了。

參考文獻

[1] 龐瀾.8051微控制器線上升級軟體的方法.http://www.mcublog.com/more.asp?name=MCUBLOG&id=3111.中國電子工程師部落格站
[2] 在WinForm中使用Web Services來實現軟體自動升級( Auto Update )(C#)。http://www.cnblogs.com/x369/articles/105656.html .部落格園
[3] 可複用的自動升級系統實現 。http://www.cnblogs.com/cdo/archive/2005/09/06/231229.html.部落格園
[4] 用VB6.0編寫自我升級的程式。
[5] .NET後臺智慧傳輸服務實現自動更新(上). 線上

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14490/viewspace-1008797/,如需轉載,請註明出處,否則將追究法律責任。

相關文章