長期以來,廣大程序員爲究竟是使用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,做用是做爲一個升級用的模板,顯示須要升級的信息。
名稱爲AutoUpdater.xml,做用是做爲一個升級用的模板,顯示須要升級的信息。
程序員
01 |
<?xml version="1.0"?> //xml版本號 |
03 |
<URLAddres URL="http://192.168.198.113/vbroker/log/"/>//升級文件所在服務器端的網址 |
05 |
<UpdateTime Date = "2005-02-02"/> //升級文件的更新日期 |
06 |
<Version Num = "1.0.0.1"/> //升級文件的版本號 |
08 |
<UpdateFileList> //升級文件列表 |
09 |
<UpdateFile FileName = "aa.txt"/> //共有三個文件需升級 |
10 |
<UpdateFile FileName = "VB40.rar"/> |
11 |
<UpdateFile FileName = "VB4-1.CAB"/> |
14 |
<ReStart Allow = "Yes"/> //容許從新啓動應用程序 |
15 |
<AppName Name = "TIMS.exe"/> //啓動的應用程序名 |
從以上XML文檔中能夠得知升級文檔所在服務器端的地址、升級文檔的更新日期、須要升級的文件列表,其中共有三個文件需升級:aa.txt、VB40.rar、VB4-1.CAB。以及是否容許從新啓動應用程序和從新啓動的應用程序名。
4.2 獲取客戶端應用程序及服務器端升級程序的最近一次更新日期
經過GetTheLastUpdateTime()函數來實現。 編程
01 |
private string GetTheLastUpdateTime( string Dir) |
03 |
string LastUpdateTime = "" ; |
04 |
string AutoUpdaterFileName = Dir + @"\AutoUpdater.xml" ; |
05 |
if (!File.Exists(AutoUpdaterFileName)) |
08 |
FileStream myFile = new FileStream(AutoUpdaterFileName,FileMode.Open); |
10 |
XmlTextReader xml = new XmlTextReader(myFile); |
13 |
if (xml.Name == "UpdateTime" ) |
16 |
LastUpdateTime = xml.GetAttribute( "Date" ); |
經過XmlTextReader打開XML文檔,讀取更新時間從而獲取Date對應的值,即服務器端升級文件的最近一次更新時間。 數組
函數調用實現:
//獲取客戶端指定路徑下的應用程序最近一次更新時間
string thePreUpdateDate = GetTheLastUpdateTime(Application.StartupPath);
Application.StartupPath指客戶端應用程序所在的路徑。
//得到從服務器端已下載文檔的最近一次更新日期
string theLastsUpdateDate = GetTheLastUpdateTime(theFolder.FullName);
theFolder.FullName指在升級文檔下載到客戶機上的臨時文件夾所在的路徑。
4.3 比較日期
客戶端應用程序最近一次更新日期與服務器端升級程序的最近一次更新日期進行比較。
服務器
02 |
string theLastsUpdateDate = GetTheLastUpdateTime(theFolder.FullName); |
03 |
if (thePreUpdateDate != "" ) |
06 |
if (Convert.ToDateTime(thePreUpdateDate)>=Convert.ToDateTime(theLastsUpdateDate)) |
08 |
MessageBox.Show( "當前軟件已是最新的,無需更新!" , "系統提示" ,MessageBoxButtons.OK,MessageBoxIcon.Information); |
12 |
this .labDownFile.Text = "下載更新文件" ; |
13 |
this .labFileName.Refresh(); |
14 |
this .btnCancel.Enabled = true ; |
15 |
this .progressBar.Position = 0; |
16 |
this .progressBarTotal.Position = 0; |
17 |
this .progressBarTotal.Refresh(); |
18 |
this .progressBar.Refresh(); |
21 |
ArrayList List = GetDownFileList(GetTheUpdateURL(),theFolder.FullName); |
22 |
string [] urls = new string [List.Count]; |
將客戶端升級的應用程序的日期與服務器端下載的應用程序日期進行比較,若是前者大於後者,則不更新;若是前者小於後者,則經過動態數組獲取下載文件的列表,開始下載文件。
4.2 獲取客戶端應用程序及服務器端升級程序的最近一次更新日期
經過GetTheLastUpdateTime()函數來實現。
網絡
01 |
private string GetTheLastUpdateTime( string Dir) |
03 |
string LastUpdateTime = "" ; |
04 |
string AutoUpdaterFileName = Dir + @"\AutoUpdater.xml" ; |
05 |
if (!File.Exists(AutoUpdaterFileName)) |
08 |
FileStream myFile = new FileStream(AutoUpdaterFileName,FileMode.Open); |
10 |
XmlTextReader xml = new XmlTextReader(myFile); |
13 |
if (xml.Name == "UpdateTime" ) |
16 |
LastUpdateTime = xml.GetAttribute( "Date" ); |
經過XmlTextReader打開XML文檔,讀取更新時間從而獲取Date對應的值,即服務器端升級文件的最近一次更新時間。
函數調用實現:
//獲取客戶端指定路徑下的應用程序最近一次更新時間
string thePreUpdateDate = GetTheLastUpdateTime(Application.StartupPath);
Application.StartupPath指客戶端應用程序所在的路徑。
//得到從服務器端已下載文檔的最近一次更新日期
string theLastsUpdateDate = GetTheLastUpdateTime(theFolder.FullName);
theFolder.FullName指在升級文檔下載到客戶機上的臨時文件夾所在的路徑。
4.3 比較日期
客戶端應用程序最近一次更新日期與服務器端升級程序的最近一次更新日期進行比較。 函數
02 |
string theLastsUpdateDate = GetTheLastUpdateTime(theFolder.FullName); |
03 |
if (thePreUpdateDate != "" ) |
06 |
if (Convert.ToDateTime(thePreUpdateDate)>=Convert.ToDateTime(theLastsUpdateDate)) |
08 |
MessageBox.Show( "當前軟件已是最新的,無需更新!" , "系統提示" ,MessageBoxButtons.OK,MessageBoxIcon.Information); |
12 |
this .labDownFile.Text = "下載更新文件" ; |
13 |
this .labFileName.Refresh(); |
14 |
this .btnCancel.Enabled = true ; |
15 |
this .progressBar.Position = 0; |
16 |
this .progressBarTotal.Position = 0; |
17 |
this .progressBarTotal.Refresh(); |
18 |
this .progressBar.Refresh(); |
2 |
ArrayList List = GetDownFileList(GetTheUpdateURL(),theFolder.FullName); |
3 |
string [] urls = new string [List.Count]; |
將客戶端升級的應用程序的日期與服務器端下載的應用程序日期進行比較,若是前者大於後者,則不更新;若是前者小於後者,則經過動態數組獲取下載文件的列表,開始下載文件。
經過BatchDownload()函數來實現。升級程序檢測舊的主程序是否活動,若活動則關閉舊的主程序;刪除舊的主程序,拷貝臨時文件夾中的文件到相應的位置;檢查主程序的狀態,若狀態爲活動的,則啓動新的主程序。
網站
01 |
private void BatchDownload( object data) |
03 |
this .Invoke( this .activeStateChanger, new object []{ true , false }); |
06 |
DownloadInstructions instructions = (DownloadInstructions) data; |
08 |
using (BatchDownloader bDL = new BatchDownloader()) |
10 |
bDL.CurrentProgressChanged += new DownloadProgressHandler( this .SingleProgressChanged); |
11 |
bDL.StateChanged += new DownloadProgressHandler( this .StateChanged); |
12 |
bDL.FileChanged += new DownloadProgressHandler(bDL_FileChanged); |
13 |
bDL.TotalProgressChanged += new DownloadProgressHandler(bDL_TotalProgressChanged); |
14 |
bDL.Download(instructions.URLs, instructions.Destination, (ManualResetEvent) this .cancelEvent); |
21 |
this .Invoke( this .activeStateChanger, new object []{ false , false }); |
22 |
this .labFileName.Text = "" ; |
27 |
this .labDownFile.Text = "正在關閉程序...." ; |
28 |
System.Diagnostics.Process[]proc=System.Diagnostics.Process.GetProcessesByName( "TIMS" ); |
30 |
foreach (System.Diagnostics.Process pro in proc) |
34 |
DirectoryInfo theFolder= new DirectoryInfo(Path.GetTempPath()+"JurassicUpdate"); |
37 |
foreach (FileInfo theFile in theFolder.GetFiles()) |
40 |
if (File.Exists(Application.StartupPath + \\"+Path.GetFileName(theFile.FullName))) |
41 |
File.Delete(Application.StartupPath + "\\" +Path.GetFileName(theFile.FullName)); |
43 |
File.Move(theFile.FullName,Application.StartupPath + \\"+Path.GetFileName(theFile.FullName)); |
47 |
this .labDownFile.Text = "正在啓動程序...." ; |
48 |
System.Diagnostics.Process.Start(Application.StartupPath + "\\" + "TIMS.exe" ); |