本通用自動更新模塊適合所有Windows桌面程序的自動更新,不論語言,無論Winform還是wpf。
一、工作流程:
1. 主程序A調起升級程序B
2. B從服務器獲取更新程序列表,打印更新信息。
3. B殺死A進程(此步驟可以放在步驟2~5任意位置)
4. B根據更新信息中指示的地址,下載更新程序包(.zip文件)
5. 解壓縮.zip文件到一個新創建的文件夾
6. 將解壓后的文件拷貝到原始文件目錄,做替換。
7. 刪除下載的.zip文件以及解壓后創建的文件夾
8. B打開A
二、源碼介紹:
升級程序B的實現:
更新信息列表用於存儲版本信息,以及更新說明信息。通常為json或xml文件。本文為json文件。
存儲列表信息的類
public class UpdateItem { public string Version { get; set; } //版本號 public string UpdateContent { get; set; } //更新信息 public string DownloadUri { get; set; } //更新包的下載地址 public string Time { get; set; } //更新時間 public string Size { get; set; } //更新包大小 }
獲取更新信息使用WebClient.DownloadData(Uri),其中使用Newtonsoft.Json進行json序列化及反序列化。
WebClient client = new WebClient(); byte[] data = client.DownloadData(uri); //json轉為UpdateItem類對象 UpdateInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<UpdateItem>(Encoding.UTF8.GetString(data));
獲取更新信息以后在界面上進行輸出。
下面介紹一下生成更新信息的json文件並使用FTP上傳到服務器的代碼。json文件也可以手動寫,手動上傳。不是重點,不想看可以跳到下一部分。
UpdateItem UpdateInfo = new UpdateItem(); ... //賦值 string json = JsonConvert.SerializeObject(UpdateInfo); //連接服務器 FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://..."); request.Method = WebRequestMethods.Ftp.UploadFile; request.Credentials = new NetworkCredential("賬號", "密碼"); // 復制字符 byte[] fileContents = Encoding.UTF8.GetBytes(json); request.ContentLength = fileContents.Length; Stream requestStream = request.GetRequestStream(); requestStream.Write(fileContents, 0, fileContents.Length); requestStream.Close(); FtpWebResponse response = (FtpWebResponse)request.GetResponse(); response.Close();
下載更新包zip文件
下載使用WebClient.DownloadFile(Uri, string)
string dir = System.IO.Directory.GetCurrentDirectory(); //程序所在文件夾路徑 string zipfile = System.IO.Path.Combine(dir, "file.zip"); //下載后zip文件的完整路徑 WebClient client = new WebClient(); client.DownloadFile(UpdateInfo.DownloadUri, zipfile); return true;
解壓縮zip文件
解壓縮需要用到System.IO.Compression.ZipFile,需要.NET4.5及以上。如果用不了(找不到ZipFile),請檢查是否已添加引用System.IO.Compression.FileSystem程序集。
代碼只有一行,兩個參數分別為zip文件完整路徑,解壓后的文件夾完整路徑。
ZipFile.ExtractToDirectory(zipfile, extractPath);
使用第三方壓縮軟件生成的.zip文件有可能會解壓失敗,建議使用下方代碼生成。兩個參數分別為需要壓縮的文件夾完整路徑,生成的zip文件完整路徑。
ZipFile.CreateFromDirectory(startPath, zipfile);
拷貝解壓后的文件到原始目錄
foreach (string item in Directory.GetFiles(extractPath)) { File.Copy(item, System.IO.Path.Combine(dir, System.IO.Path.GetFileName(item)), true); }
刪除臨時文件
File.Delete(zipfile); DirectoryInfo di = new DirectoryInfo(extractPath); di.Delete(true);
B進程殺死A進程
string appname = "file"; //A名字,不要路徑,不要.exe Process[] processes = Process.GetProcessesByName(appname); foreach (var p in processes) p.Kill();
B進程調起A進程
System.Diagnostics.Process.Start(@"path"); //完整路徑
主要流程就是這些,建議用異步操作實現。
現在上號比較少,私信很多要代碼的都沒回…以后都會把完整代碼附上的。
下面是完整代碼。
private Uri uri = new Uri(@"http://..."); //更新信息列表文件路徑 private UpdateItem UpdateInfo; //UpdateItem類的定義在前面第一部分說明 public MainWindow() { InitializeComponent(); LoadingData(); } //用戶按下更新按鈕 private async void Button_Click(object sender, RoutedEventArgs e) { //下載 Tb_State.Text = "正在下載新版本文件,請耐心等待"; string dir = System.IO.Directory.GetCurrentDirectory(); string zipfile = System.IO.Path.Combine(dir, "File.zip"); bool success = await Task.Run(() => { try { WebClient client = new WebClient(); client.DownloadFile(UpdateInfo.DownloadUri, zipfile); return true; } catch (Exception) { return false; } }); if (success) Tb_State.Text = "文件已下載,正在復制文件"; else { Tb_State.Text = "下載新版本文件失敗,請重試"; return; } //殺死主程序進程 string appname = "File"; Process[] processes = Process.GetProcessesByName(appname); foreach (var p in processes) p.Kill(); //解壓縮+拷貝+刪除 bool success2 = await Task.Run(() => { try { string extractPath = System.IO.Path.Combine(dir, "NewVersion"); ZipFile.ExtractToDirectory(zipfile, extractPath); foreach (string item in Directory.GetFiles(extractPath)) File.Copy(item, System.IO.Path.Combine(dir, System.IO.Path.GetFileName(item)), true); File.Delete(zipfile); DirectoryInfo di = new DirectoryInfo(extractPath); di.Delete(true); return true; } catch (Exception) { return false; } }); if (success2) Tb_State.Text = "更新完成,您可以點擊下方按鈕啟動應用"; else Tb_State.Text = "復制更新文件出錯,請重試"; } //讀取更新列表文件 private async Task<bool> LoadingData() { Tb_State.Text = "正在下載更新文件信息"; bool success = await Task.Run(() => { try { WebClient client = new WebClient(); byte[] data = client.DownloadData(uri); UpdateInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<UpdateItem>(Encoding.UTF8.GetString(data)); return true; } catch (Exception) { return false; } }); if (success) Tb_State.Text = "已獲取新版本信息,可進行更新"; else Tb_State.Text = "無法獲取更新信息"; return success; } //更新完成,更新程序B調起主程序A private void Btn_Open_Click(object sender, RoutedEventArgs e) { System.Diagnostics.Process.Start(@"D:\..."); //A程序完整路徑 }
再貼一個用於下載過程中,能實時顯示已下載文件大小的代碼
此部分需寫在下載部分之前。
System.Windows.Threading.DispatcherTimer dt = new System.Windows.Threading.DispatcherTimer(); dt.Interval = TimeSpan.FromMilliseconds(100); //100毫秒 dt.Tick += (x, y) => { if (File.Exists(zipfile) == false) return; string size = ((new FileInfo(zipfile).Length) / 1024.0 / 1024).ToString("f2"); if (download == false) //是否下載完畢 Tb_State.Text = size + "MB / " + UpdateInfo.Size; //輸出:已下載/總大小 else dt.Stop(); }; dt.Start();
代碼部分執行時,再次執行代碼可能會報錯。可能原因包括下載zip文件后未刪除再次下載報錯等。如要做項目還有很多地方需要進行判斷,如檢測路徑的合法性等。為了代碼便於閱讀,沒有加入這些錯誤檢測部分,需自行補充。幾乎所有操作前都應判斷路徑合法性,以及目標位置的文件是否存在(是否需要先刪除)等。只能說是在輸入合法的情況下,完整執行此代碼是沒有問題的。
代碼通過Visual Studio 2019測試,.NET4.5。