c# 智能升级程序代码(2)
最主要的就是Update()这个函数了。当程序调用au.Update时,首先检查当前是否开户了自动更新:
if (!config.Enabled)
??? return;
如果启用了自动更新,就需要去下载服务器配置文件了:
WebClient client = new WebClient();
string strXml = client.DownloadString(config.ServerUrl);
然后,解析服务器配置文件到一个Dictionary中:
Dictionary<string, RemoteFile> listRemotFile = ParseRemoteXml(strXml);
接下来比较服务器配置文件和本地配置文件,找出需要下载的文件和本地需要删除的文件:
List<DownloadFileInfo> downloadList = new List<DownloadFileInfo>();
//某些文件不再需要了,删除
List<LocalFile> preDeleteFile = new List<LocalFile>();
foreach (LocalFile file in config.UpdateFileList)
{
??? if (listRemotFile.ContainsKey(file.Path))
??? {
??????? RemoteFile rf = listRemotFile[file.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);
??? }
??? else
??? {
??????? preDeleteFile.Add(file);
??? }
}
foreach (RemoteFile file 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(downloadList);
??? 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(downloadList);
??? }
}
我们再来看一下StartDownload函数
private void StartDownload(List<DownloadFileInfo> downloadList)
{
??? DownloadProgress dp = new DownloadProgress(downloadList);
??? if (dp.ShowDialog() == DialogResult.OK)
??? {
??????? //更新成功
??????? config.SaveConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FILENAME));
??????? if (bNeedRestart)
??????? {
??????????? MessageBox.Show("程序需要重新启动才能应用更新,请点击确定重新启动程序。", "自动更新", MessageBoxButtons.OK, MessageBoxIcon.Information);
??????????? Process.Start(Application.ExecutablePath);
??????????? Environment.Exit(0);
??????? }
??? }
}
在这个函数中,先调用DownloadProgress下载所有需要下载的文件,然后更新本地配置文件,最后,如果发现某些更新文件需要重新启动应用程序的话,会提示用户重新启动程序。
至此,AutoUpdater这个类的使命就完成了,其实,整个的升级过程也就完成了。(废话)。
最后,我们来看一下这个组件是如何下载更新文件的
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.IO;
using System.Diagnostics;
namespace Iyond.Utility
{
??? public partial class DownloadProgress : Form
??? {
??????? private bool isFinished = false;
??????? private List<DownloadFileInfo> downloadFileList = null;
??????? private ManualResetEvent evtDownload = null;
??????? private ManualResetEvent evtPerDonwload = null;
??????? private WebClient clientDownload = null;
??????? public DownloadProgress(List<DownloadFileInfo> downloadFileList)
??????? {
??????????? InitializeComponent();
??????????? this.downloadFileList = downloadFileList;
??????? }
??????? private void OnFormClosing(object sender, FormClosingEventArgs e)
??????? {
??????????? if (!isFinished && DialogResult.No == MessageBox.Show("当前正在更新,是否取消?", "自动升级", MessageBoxButtons.YesNo, MessageBoxIcon.Question))
??????????? {
??????????????? e.Cancel = true;
??????????????? return;
??????????? }
??????????? else
??????????? {
??????????????? if (clientDownload != null)
??????????????????? clientDownload.CancelAsync();
??????????????? evtDownload.Set();
??????????????? evtPerDonwload.Set();
??????????? }
??????? }
??????? private void OnFormLoad(object sender, 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()
??????? {
??????????? 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.downloadFileList[0];
??????????????? //Debug.WriteLine(String.Format("Start Download:{0}", file.FileName));
??????????????? this.ShowCurrentDownloadFileName(file.FileName);
??????????????? //下载
??????????????? clientDownload = new WebClient();
??????????????? clientDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
??????????????? clientDownload.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadFileCompleted);
??????????????? evtPerDonwload.Reset();
??????????????? clientDownload.DownloadFileAsync(new Uri(file.DownloadUrl), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.FileFullName + ".tmp"), file);
??????????????? //等待下载完成
??????????????? evtPerDonwload.WaitOne();
??????????????? clientDownload.Dispose();
??????????????? clientDownload = null;
??????????????? //移除已下载的文件
??????????????? this.downloadFileList.Remove(file);
??????????? }
??????????? //Debug.WriteLine("All Downloaded");
??????????? if (this.downloadFileList.Count == 0)
??????????????? Exit(true);
??????????? else
??????????????? Exit(false);
??????????? evtDownload.Set();
??????? }
??????? void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
??????? {
??????????? DownloadFileInfo file = e.UserState as DownloadFileInfo;
??????????? nDownloadedTotal += file.Size;
??????????? this.SetProcessBar(0, (int)(nDownloadedTotal * 100 / total));
??????????? //Debug.WriteLine(String.Format("Finish Download:{0}", file.FileName));
??????????? //替换现有文件
??????????? string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.FileFullName);
??????????? if (File.Exists(filePath))
??????????? {
??????????????? if (File.Exists(filePath + ".old"))
??????????????????? File.Delete(filePath + ".old");
??????????????? File.Move(filePath, filePath + ".old");
??????????? }
??????????? File.Move(filePath + ".tmp", filePath);
??????????? //继续下载其它文件
??????????? evtPerDonwload.Set();
??????? }
??????? void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
??????? {
??????????? this.SetProcessBar(e.ProgressPercentage, (int)((nDownloadedTotal + e.BytesReceived) * 100 / total));
??????? }
??????? delegate 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
??????????? {
??????????????? 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);
??????????????? this.Invoke(cb, new object[] { current, total });
??????????? }
??????????? else
??????????? {
??????????????? this.progressBarCurrent.Value = current;
??????????????? this.progressBarTotal.Value = total;
??????????? }
??????? }
??????? delegate void ExitCallBack(bool success);
??????? private void Exit(bool success)
??????? {
??????????? if (this.InvokeRequired)
??????????? {
??????????????? ExitCallBack cb = new ExitCallBack(Exit);
??????????????? this.Invoke(cb, new object[] { success });
??????????? }
??????????? else
??????????? {
??????????????? this.isFinished = success;
??????????????? this.DialogResult = success ? DialogResult.OK : DialogResult.Cancel;
??????????????? this.Close();
??????????? }
??????? }
??????? private void OnCancel(object sender, EventArgs e)
??????? {
??????????? evtDownload.Set();
??????????? evtPerDonwload.Set();
??????? }
??? }
}
在构造函数中,将要下载的文件列表传进来
public DownloadProgress(List<DownloadFileInfo> downloadFileList)
{
??? InitializeComponent();
??? this.downloadFileList = downloadFileList;
}
在Form的Load事件中,启动下载线程,开始下载。
private void OnFormLoad(object sender, EventArgs e)
{
??? evtDownload = new ManualResetEvent(true);
??? evtDownload.Reset();
??? Thread t = new Thread(new ThreadStart(ProcDownload));
??? t.Name = "download";
??? t.Start();
}
下载线程没什么特殊的,使用了WebClient的异步下载文件函数DownloadFileAsync,并且注册了两个事件,分别负责下载进度显示和下载完成后的处理:
clientDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
clientDownload.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadFileCompleted);
大家看一下就明白了。
private void ProcDownload()
{
??? 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.downloadFileList[0];
??????? //Debug.WriteLine(String.Format("Start Download:{0}", file.FileName));
??????? this.ShowCurrentDownloadFileName(file.FileName);
??????? //下载
??????? clientDownload = new WebClient();
??????? clientDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
??????? clientDownload.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadFileCompleted);
??????? evtPerDonwload.Reset();
??????? clientDownload.DownloadFileAsync(new Uri(file.DownloadUrl), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.FileFullName + ".tmp"), file);
??????? //等待下载完成
??????? evtPerDonwload.WaitOne();
??????? clientDownload.Dispose();
??????? clientDownload = null;
??????? //移除已下载的文件
??????? this.downloadFileList.Remove(file);
??? }
??? //Debug.WriteLine("All Downloaded");
??? if (this.downloadFileList.Count == 0)
??????? Exit(true);
??? else
??????? Exit(false);
??? evtDownload.Set();
}
最后,在OnDownloadFileCompleted函数中进行最后的处理。包括备份原文件,替换现有文件等。
void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
??? DownloadFileInfo file = e.UserState as DownloadFileInfo;
??? nDownloadedTotal += file.Size;
??? this.SetProcessBar(0, (int)(nDownloadedTotal * 100 / total));
??? //Debug.WriteLine(String.Format("Finish Download:{0}", file.FileName));
??? //替换现有文件
??? string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.FileFullName);
??? if (File.Exists(filePath))
??? {
??????? if (File.Exists(filePath + ".old"))
??????????? File.Delete(filePath + ".old");
??????? File.Move(filePath, filePath + ".old");
??? }
??? File.Move(filePath + ".tmp", filePath);
??? //继续下载其它文件
??? evtPerDonwload.Set();
}
其它的函数只是一些显示进度条和下载信息的,这里就不再详细介绍了。