1、最近单位开发一个项目,其中需要用到自动升级功能。因为自动升级是一个比较常用的功能,可能会在很多程序中用到,于是,我就想写一个自动升级的组件,在应用程序中,只需要引用这个自动升级组件,并添加少量代码,即可实现自动升级功能。因为我们的程序中可能包含多个 exe 或者 dll 文件,所以要支持多文件的更新。首先,要确定程序应该去哪里下载需要升级的文件。我选择了到指定的网站上去下载,这样比较简单,也通用一些。在这个网站上,需要放置一个当前描述最新文件列表的文件,我们估且叫它服务器配置文件。这个文件保存了当前最新文件的版本号(lastver),大小(size),下载地址(url),本地文件的保存路径(p
2、ath),还有当更新了这个文件后,程序是否需要重新启动(needRestart)。这个文件大致如下:updateservice.xml 同时,客户端也保存了一个需要升级的本地文件的列表,形式和服务器配置文件差不多,我们叫它本地配置文件。其中,节点表示是否启用自动升级功能,表示服务器配置文件的地址。update.config truehttp:/ Program.cs 的 Main 函数中:STAThreadstatic void Main()Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefaul
3、t(false);AutoUpdater au = new AutoUpdater();tryau.Update();catch (WebException exp)MessageBox.Show(String.Format(“无法找到指定资源nn0“, exp.Message), “自动升级“, MessageBoxButtons.OK, MessageBoxIcon.Error);catch (XmlException exp)MessageBox.Show(String.Format(“下载的升级文件有错误nn0“, exp.Message), “自动升级“, MessageBoxBut
4、tons.OK, MessageBoxIcon.Error);catch (NotSupportedException exp)MessageBox.Show(String.Format(“升级地址配置错误nn0“, exp.Message), “自动升级“, MessageBoxButtons.OK, MessageBoxIcon.Error);catch (ArgumentException exp)MessageBox.Show(String.Format(“下载的升级文件有错误nn0“, exp.Message), “自动升级“, MessageBoxButtons.OK, Messa
5、geBoxIcon.Error);catch (Exception exp)MessageBox.Show(String.Format(“升级过程中发生错误nn0“, exp.Message), “自动升级“, MessageBoxButtons.OK, MessageBoxIcon.Error);Application.Run(new MainUI();如上所示,只需要简单的几行代码,就可以实现自动升级功能了。软件运行截图:下面,我们来详细说一下这个自动升级组件的实现。先看一下类图:AutoUpdater:自动升级的管理类,负责整体的自动升级功能的实现。Config:配置类,负责管理本地配置
6、文件。DownloadConfirm:一个对话框,向用户显示需要升级的文件的列表,并允许用户选择是否马上升级。DownloadFileInfo:要下载的文件的信息DownloadProgress:一个对话框,显示下载进度。DownloadProgress.ExitCallBack,DownloadProgress.SetProcessBarCallBack,DownloadProgress.ShowCurrentDownloadFileNameCallBack:由于.NET2.0 不允许在一个线程中访问另一个线程的对象,所以需要通过委托来实现。LocalFile:表示本地配置文件中的一个文件R
7、emoteFile:表示服务器配置文件中的一个文件。UpdateFileList:一个集合,从 List继承我们先整体看一下 AutoUpdater.cs:public class AutoUpdaterconst string FILENAME = “update.config“;private Config config = null;private bool bNeedRestart = false;public AutoUpdater()config = Config.LoadConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirecto
8、ry, FILENAME);/*/ / 检查新版本/ / 无法找到指定资源/ 升级地址配置错误/ 下载的升级文件有错误/ 下载的升级文件有错误/ 未知错误/ public void Update()if (!config.Enabled)return;/*/* 请求 Web 服务器,得到当前最新版本的文件列表,格式同本地的 FileList.xml。* 与本地的 FileList.xml 比较,找到不同版本的文件* 生成一个更新文件列表,开始 DownloadProgress* * * * path 为相对于应用程序根目录的相对目录位置,包括文件名*/WebClient client = ne
9、w WebClient();string strXml = client.DownloadString(config.ServerUrl);Dictionary listRemotFile = ParseRemoteXml(strXml);List downloadList = new List();/某些文件不再需要了,删除List preDeleteFile = new List();foreach (LocalFile file in config.UpdateFileList)if (listRemotFile.ContainsKey(file.Path)RemoteFile rf =
10、 listRemotFilefile.Path;if (rf.LastVer != file.LastVer)downloadList.Add(new DownloadFileInfo(rf.Url, file.Path, rf.LastVer, rf.Size);file.LastVer = rf.LastVer;file.Size = rf.Size;if (rf.NeedRestart)bNeedRestart = true;listRemotFile.Remove(file.Path);elsepreDeleteFile.Add(file);foreach (RemoteFile fi
11、le in listRemotFile.Values)downloadList.Add(new DownloadFileInfo(file.Url, file.Path, file.LastVer, file.Size);config.UpdateFileList.Add(new LocalFile(file.Path, file.LastVer, file.Size);if (file.NeedRestart)bNeedRestart = true;if (downloadList.Count 0)DownloadConfirm dc = new DownloadConfirm(downlo
12、adList);if (this.OnShow != null)this.OnShow();if (DialogResult.OK = dc.ShowDialog()foreach (LocalFile file in preDeleteFile)string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.Path);if (File.Exists(filePath)File.Delete(filePath);config.UpdateFileList.Remove(file);StartDownload
13、(downloadList);private void StartDownload(List downloadList)DownloadProgress dp = new DownloadProgress(downloadList);if (dp.ShowDialog() = DialogResult.OK)/更新成功config.SaveConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FILENAME);if (bNeedRestart)MessageBox.Show(“程序需要重新启动才能应用更新,请点击确定重新启动程序
14、。“, “自动更新“, MessageBoxButtons.OK, MessageBoxIcon.Information);Process.Start(Application.ExecutablePath);Environment.Exit(0);private Dictionary ParseRemoteXml(string xml)XmlDocument document = new XmlDocument();document.LoadXml(xml);Dictionary list = new Dictionary();foreach (XmlNode node in document
15、.DocumentElement.ChildNodes)list.Add(node.Attributes“path“.Value, new RemoteFile(node);return list;public event ShowHandler OnShow;在构造函数中,我们先要加载配置文件:public AutoUpdater()config = Config.LoadConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FILENAME);最主要的就是 Update()这个函数了。当程序调用 au.Update 时,首先检
16、查当前是否开户了自动更新: if (!config.Enabled)return;如果启用了自动更新,就需要去下载服务器配置文件了: WebClient client = new WebClient();string strXml = client.DownloadString(config.ServerUrl);然后,解析服务器配置文件到一个 Dictionary 中:Dictionary listRemotFile = ParseRemoteXml(strXml);接下来比较服务器配置文件和本地配置文件,找出需要下载的文件和本地需要删除的文件:List downloadList = new
17、 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.Path, rf.LastVer, rf.Size);file.LastV
18、er = rf.LastVer;file.Size = rf.Size;if (rf.NeedRestart)bNeedRestart = 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.Size);config.UpdateFileList.Add(new Lo
19、calFile(file.Path, file.LastVer, file.Size);if (file.NeedRestart)bNeedRestart = true;如果发现有需要下载的文件,则向用户显示这些文件,并提示其是否马上更新。如果用户选择了马上更新,则先删除本地不再需要的文件,然后开始下载更新文件。 if (downloadList.Count 0)DownloadConfirm dc = new DownloadConfirm(downloadList);if (this.OnShow != null)this.OnShow();if (DialogResult.OK = dc
20、.ShowDialog()foreach (LocalFile file in preDeleteFile)string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.Path);if (File.Exists(filePath)File.Delete(filePath);config.UpdateFileList.Remove(file);StartDownload(downloadList);我们再来看一下 StartDownload 函数private void StartDownload(List downloadList)DownloadProgress dp = new DownloadProgress(downloadList);if (dp.ShowDialog() = DialogResult.OK)