1、在 WinForm 应用程序中实现自动升级功能首先,要确定程序应去哪里下载需要升级的文件。我选择了到指定的网站上去下载,这样比较简单,也通用一些。在网站上,需放置一个当前描述最新文件列表的文件,我估且叫它服务器配置文件。这个文件保存了当前最新文件的版本号(lastversion),大小(size),下载地址(url),本地文件的保存路径(path),还有更新后,程序是否需要重新启动(needRestart)。updateService.xml同时,客户端也保存了一个需要升级的本地文件的列表,形式和服务器配置文件差不多,我们叫它本地配置文件。其中,节点表示是否启用自动升级功能,表示服务器配置文件
2、的地址。update.configtruehttp:/ void Main() Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);AutoUpdater au = new AutoUpdater();try au.Update(); catch (System.Net.WebException exception) MessageBox.Show(“无法找到指定资源nn“ + exception.Message, “自动升级“, MessageBoxButtons.OK,
3、MessageBoxIcon.Error); catch (System.Xml.XmlException exception) MessageBox.Show(“下载的升级文件有错误nn“ + exception.Message, “自动升级“, MessageBoxButtons.OK, MessageBoxIcon.Error); catch (System.NotSupportedException exception) MessageBox.Show(“升级地址配置错误nn“ + exception.Message, “自动升级“, MessageBoxButtons.OK, Mes
4、sageBoxIcon.Error); catch (System.ArgumentException exception) MessageBox.Show(“下载的升级文件有错误nn“ + exception.Message, “自动升级“, MessageBoxButtons.OK, MessageBoxIcon.Error); catch (System.Exception exception) MessageBox.Show(“升级过程中发生错误nn“ + exception.Message, “自动升级“, MessageBoxButtons.OK, MessageBoxIcon.E
5、rror); 如上所示,只需要简单的几行代码,就可以实现自动升级功能了。软件运行截图:AutoUpdater.csusing System;using System.Collections.Generic;using System.Diagnostics;using System.IO;using System.Net;using System.Text;using System.Windows.Forms;using System.Xml;using System.Xml.Serialization;namespace MyUpdate / / 自动升级的管理类,负责整体的自动升级功能的实现
6、/ public class AutoUpdater public event ShowHandler OnShow;public delegate void ShowHandler();private const string fileName = “update.config“;private readonly string downLoadXML = “;private Config config = null;private bool needRestart = false;public AutoUpdater() / 加载配置文件config = Config.LoadConfig(
7、Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);downLoadXML = Application.StartupPath + “ + “updateService.xml“;/ / 检查新版本/ / 无法找到指定资源/ 升级地址配置错误/ 下载的升级文件有错误/ 下载的升级文件有错误/ 未知错误public void Update() / 是否开启了自动更新if (!config.Enabled)return;/ 请求Web服务器,得到当前最新版本的文件列表,格式同本地的update.config。与本地的updat
8、e.config比较,找到不同版本的文件,生成一个更新文件列表,开始DownloadProgress/ 如果启用了自动更新,就需要下载服务器配置文件WebClient client = new WebClient();client.Encoding = Encoding.Default;client.DownloadFile(config.ServerUrl, downLoadXML);/ 解析服务器配置文件到Dictionary中Dictionary listRemotFile = ParseRemoteXml(downLoadXML);/ 比较服务器配置文件和本地配置文件,找出需要下载的文
9、件和本地需要删除的文件List downloadList = new List();/ 某些文件不再需要了,删除List preDeleteFile = new List();foreach (LocalFile file in config.UpdateFileList) if (listRemotFile.ContainsKey(file.Path) RemoteFile rf = listRemotFilefile.Path;if (rf.LastVer != file.LastVer) downloadList.Add(new DownloadFileInfo(rf.Url, file
10、.Path, rf.LastVer, rf.Size);file.LastVer = rf.LastVer;file.Size = rf.Size;if (rf.NeedRestart)needRestart = true;listRemotFile.Remove(file.Path);elsepreDeleteFile.Add(file);foreach (RemoteFile file in listRemotFile.Values) downloadList.Add(new DownloadFileInfo(file.Url, file.Path, file.LastVer, file.
11、Size);config.UpdateFileList.Add(new LocalFile(file.Path, file.LastVer, file.Size);if (file.NeedRestart)needRestart = true;/ 如果发现有需要下载的文件,则向用户显示这些文件,并提示其是否马上更新。如果用户选择了马上更新,则先删除本地不再需要的文件,然后开始下载更新文件if (downloadList.Count 0) DownloadConfirm dc = new DownloadConfirm(downloadList);if (this.OnShow != null)
12、this.OnShow();if (DialogResult.OK = dc.ShowDialog() Process proc = Process.GetProcessesByName(“);/关闭原有应用程序的所有进程foreach (Process pro in proc)pro.Kill();foreach (LocalFile file in preDeleteFile) string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.Path);if (File.Exists(filePath)F
13、ile.Delete(filePath);config.UpdateFileList.Remove(file);StartDownload(downloadList);private Dictionary ParseRemoteXml(string xml) XmlDocument document = new XmlDocument();document.Load(xml);Dictionary list = new Dictionary();foreach (XmlNode node in document.DocumentElement.ChildNodes)list.Add(node.
14、Attributes“path“.Value, new RemoteFile(node);return list;/ / 先调用DownloadProgress下载所有需要下载的文件,然后更新本地配置文件,最后,如果发现某些更新文件需要重新启动应用程序的话,会提示用户重新启动程序。/ private void StartDownload(List downloadList) DownloadProgress dp = new DownloadProgress(downloadList);if (dp.ShowDialog() = DialogResult.OK) / 更新成功config.Sa
15、veConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);if (needRestart) MessageBox.Show(“程序需要重新启动才能应用更新,请点击确定重新启动程序。“, “自动更新“, MessageBoxButtons.OK, MessageBoxIcon.Information);Process.Start(Application.ExecutablePath);Environment.Exit(0);/ / 配置类,负责管理本地配置文件/ public class Config priva
16、te bool enabled = true;public bool Enabled get return enabled; set enabled = value; private string serverUrl = “;public string ServerUrl get return serverUrl; set serverUrl = value; private List updateFileList = new List();public List UpdateFileList get return updateFileList; set updateFileList = va
17、lue; public static Config LoadConfig(string file) XmlSerializer xs = new XmlSerializer(typeof(Config);StreamReader sr = new StreamReader(file);Config config = xs.Deserialize(sr) as Config;sr.Close();return config;public void SaveConfig(string file) XmlSerializer xs = new XmlSerializer(typeof(Config)
18、;StreamWriter sw = new StreamWriter(file);xs.Serialize(sw, this);sw.Close();/ / 表示本地配置文件中的一个文件/ public class LocalFile private string path = “;private string lastver = “;private int size = 0;XmlAttribute(“path“)public string Path get return path; set path = value; XmlAttribute(“lastver“)public strin
19、g LastVer get return lastver; set lastver = value; XmlAttribute(“size“)public int Size get return size; set size = value; public LocalFile() public LocalFile(string path, string ver, int size) this.path = path;this.lastver = ver;this.size = size;/ / 表示服务器配置文件中的一个文件/ public class RemoteFile private s
20、tring path = “;private string url = “;private string lastver = “;private int size = 0;private bool needRestart = false;public string Path get return path; public string Url get return url; public string LastVer get return lastver; public int Size get return size; public bool NeedRestart get return n
21、eedRestart; public RemoteFile(XmlNode node) this.path = node.Attributes“path“.Value;this.url = node.Attributes“url“.Value;this.lastver = node.Attributes“lastver“.Value;this.size = Convert.ToInt32(node.Attributes“size“.Value);this.needRestart = Convert.ToBoolean(node.Attributes“needRestart“.Value);/
22、/ 要下载的文件的信息/ public class DownloadFileInfo string downloadUrl = “;string fileName = “;string lastver = “;int size = 0;/ / 要从哪里下载文件/ public string DownloadUrl get return downloadUrl; / / 下载完成后要放到哪里去/ public string FileFullName get return fileName; public string FileName get return Path.GetFileName(Fi
23、leFullName); public string LastVer get return lastver; set lastver = value; public int Size get return size; public DownloadFileInfo(string url, string name, string ver, int size) this.downloadUrl = url;this.fileName = name;this.lastver = ver;this.size = size;DownloadConfirm.csusing System;using Sys
24、tem.Collections.Generic;using System.ComponentModel;using System.Drawing;using System.Windows.Forms;namespace MyUpdate / / 一个对话框,向用户显示需要升级的文件的列表,并允许用户选择是否马上升级/ public class DownloadConfirm : Form List downloadFileList = null;public DownloadConfirm(List dfl) InitializeComponent();downloadFileList = d
25、fl;private void OnLoad(object sender, EventArgs e) foreach (DownloadFileInfo file in this.downloadFileList) ListViewItem item = new ListViewItem(new string file.FileName, file.LastVer, file.Size.ToString() );this.lvDownloadFile.Items.Add(item);this.Activate();this.Focus();/ / 必需的设计器变量/ private ICont
26、ainer components = null;/ / 清理所有正在使用的资源/ / 如果应释放托管资源,为true;否则为falseprotected override void Dispose(bool disposing) if (disposing base.Dispose(disposing);/ / 设计器支持所需的方法- 不要使用代码编辑器修改此方法的内容/ private void InitializeComponent() this.btnOk = new Button();this.btnCancel = new Button();this.lvDownloadFile =
27、 new ListView();this.columnHeader1 = new ColumnHeader();this.columnHeader2 = new ColumnHeader();this.columnHeader3 = new ColumnHeader();this.lblFileUpdate = new Label();this.SuspendLayout();/ / btnOk/ this.btnOk.DialogResult = DialogResult.OK;this.btnOk.Location = new Point(184, 217);this.btnOk.Name
28、 = “btnOk“;this.btnOk.Size = new Size(75, 23);this.btnOk.Text = “马上更新“;this.btnOk.UseVisualStyleBackColor = true;/ / btnCancel/ this.btnCancel.DialogResult = DialogResult.Cancel;this.btnCancel.Location = new Point(265, 217);this.btnCancel.Name = “btnCancel“;this.btnCancel.Size = new Size(75, 23);thi
29、s.btnCancel.Text = “以后再说“;this.btnCancel.UseVisualStyleBackColor = true;/ / lvDownloadFile/ this.lvDownloadFile.Columns.AddRange(new ColumnHeader this.columnHeader1,this.columnHeader2,this.columnHeader3);this.lvDownloadFile.Location = new Point(12, 24);this.lvDownloadFile.Name = “lvDownloadFile“;thi
30、s.lvDownloadFile.Size = new Size(328, 187);this.lvDownloadFile.UseCompatibleStateImageBehavior = false;this.lvDownloadFile.View = View.Details;this.lvDownloadFile.FullRowSelect = true;/ / columnHeader1/ this.columnHeader1.Text = “文件名“;this.columnHeader1.Width = 149;/ / columnHeader2/ this.columnHead
31、er2.Text = “最新版本“;this.columnHeader2.Width = 110;/ / columnHeader3/ this.columnHeader3.Text = “大小“;this.columnHeader3.Width = 65;/ / lblFileUpdate/ this.lblFileUpdate.AutoSize = true;this.lblFileUpdate.Location = new Point(12, 9);this.lblFileUpdate.Name = “lblFileUpdate“;this.lblFileUpdate.Size = ne
32、w Size(113, 12);this.lblFileUpdate.Text = “以下文件需要更新:“;/ / DownloadConfirm/ this.AcceptButton = this.btnOk;this.AutoScaleDimensions = new SizeF(6F, 12F);this.AutoScaleMode = AutoScaleMode.Font;this.CancelButton = this.btnCancel;this.ClientSize = new Size(352, 252);this.ControlBox = false;this.Control
33、s.Add(this.lblFileUpdate);this.Controls.Add(this.lvDownloadFile);this.Controls.Add(this.btnCancel);this.Controls.Add(this.btnOk);this.FormBorderStyle = FormBorderStyle.FixedDialog;this.MaximizeBox = false;this.MinimizeBox = false;this.Name = “DownloadConfirm“;this.StartPosition = FormStartPosition.C
34、enterScreen;this.Text = “发现新版本“;this.Load += new EventHandler(this.OnLoad);this.ResumeLayout(false);this.PerformLayout();private Button btnOk;private Button btnCancel;private ListView lvDownloadFile;private Label lblFileUpdate;private ColumnHeader columnHeader1;private ColumnHeader columnHeader2;pri
35、vate ColumnHeader columnHeader3;DownloadProgress.csusing System;using System.Collections.Generic;using System.ComponentModel;using System.Drawing;using System.IO;using System.Net;using System.Threading;using System.Windows.Forms;namespace MyUpdate / / 一个对话框,显示下载进度/ public partial class DownloadProgr
36、ess : Form private bool isFinished = false;private List downloadFileList = null;/ new private List downloadedFileList = null;/ end newprivate ManualResetEvent evtDownload = null;private ManualResetEvent evtPerDonwload = null;private WebClient clientDownload = null;public DownloadProgress(List downlo
37、adFileList) InitializeComponent();this.downloadFileList = downloadFileList;private void OnFormClosing(object sender, FormClosingEventArgs e) if (!isFinished return;else if (clientDownload != null)clientDownload.CancelAsync();evtDownload.Set();evtPerDonwload.Set();private void OnFormLoad(object sende
38、r, EventArgs e) evtDownload = new ManualResetEvent(true);evtDownload.Reset();Thread t = new Thread(new ThreadStart(ProcDownload);t.Name = “download“;t.Start();long total = 0;long nDownloadedTotal = 0;private void ProcDownload() / newif (downloadedFileList = null)downloadedFileList = new List();/ end
39、 new evtPerDonwload = new ManualResetEvent(false);foreach (DownloadFileInfo file in this.downloadFileList)total += file.Size;while (!evtDownload.WaitOne(0, false) if (this.downloadFileList.Count = 0)break;DownloadFileInfo file = this.downloadFileList0;this.ShowCurrentDownloadFileName(file.FileName);
40、/ 下载clientDownload = new WebClient();clientDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);clientDownload.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadFileCompleted);evtPerDonwload.Reset();clientDownload.DownloadFileAsync(ne
41、w Uri(file.DownloadUrl), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.FileFullName + “.tmp“), file);/ 等待下载完成evtPerDonwload.WaitOne();clientDownload.Dispose();clientDownload = null;if (this.downloadFileList.Count = 0)Exit(true);elseExit(false);evtDownload.Set();private void OnDownloadFile
42、Completed(object sender, AsyncCompletedEventArgs e) DownloadFileInfo file = e.UserState as DownloadFileInfo;nDownloadedTotal += file.Size;this.SetProcessBar(0, (int)(nDownloadedTotal * 100 / total);this.downloadFileList.Remove(file);/ new this.downloadedFileList.Add(file);/ end newif (this.downloadF
43、ileList.Count = 0) try foreach (DownloadFileInfo filex in downloadedFileList) string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filex.FileFullName);if (File.Exists(filePath)if (File.Exists(filePath + “.old“)File.Delete(filePath + “.old“);if (File.Exists(filePath + “.tmp“) if (Fil
44、e.Exists(filePath)File.Delete(filePath);File.Move(filePath + “.tmp“, filePath);catch (Exception ex) MessageBox.Show(ex.Message); finally foreach (DownloadFileInfo fileee in downloadedFileList) string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileee.FileFullName);if (File.Exists(
45、filePath + “.tmp“)File.Delete(filePath + “.tmp“);/ 继续下载其它文件evtPerDonwload.Set();private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) int t = (int)(nDownloadedTotal + e.BytesReceived) * 100 / total);this.SetProcessBar(e.ProgressPercentage, t 100 ? 100 : t);delegat
46、e void ShowCurrentDownloadFileNameCallBack(string name);private void ShowCurrentDownloadFileName(string name) if (this.labelCurrentItem.InvokeRequired) ShowCurrentDownloadFileNameCallBack cb = new ShowCurrentDownloadFileNameCallBack(ShowCurrentDownloadFileName);this.Invoke(cb, new object name );else
47、this.labelCurrentItem.Text = name;delegate void SetProcessBarCallBack(int current, int total);private void SetProcessBar(int current, int total) if (this.progressBarCurrent.InvokeRequired) SetProcessBarCallBack cb = new SetProcessBarCallBack(SetProcessBar);if (total current)total = current;this.Invoke(cb, new object current, total );else this.progressBarCurrent.Value = current;this.progressBarTotal.Value = total;delegate void ExitCallBack(bool success);private voi