winform更新程序代码
这是我的第一个随笔,写的不好的或者语法错误等,请大家不要见怪,首先预览下截图
自动更新一般分3部分:
1、发现更新
2、下载文件
3、复制文件
下面对以上3个过程做简单的介绍
发现更新
根据客户端的配置文件的manifestUri去读取服务器的xml文件,然后依次比较applicationId和version,如果applicationId一致且version不一致则可以更新,客户端配置文件不是app.config,而是一个自己定义的xml文件,至于为什么不用app.config文件的原因很简单,因为app.config在系统运行的时候是不能进行修改的,客户端配置文件内容大概如下:
<?xml version="1.0" encoding="utf-8"?>
<applicationUpdater applicationId="{4a1e1623-bd3c-4921-8d91-85d34a5fe518}"
manifestUri="http://127.0.0.1/manifests.xml"
version="1.04">
</applicationUpdater>
<applicationUpdater applicationId="{4a1e1623-bd3c-4921-8d91-85d34a5fe518}"
manifestUri="http://127.0.0.1/manifests.xml"
version="1.04">
</applicationUpdater>
applicationId表示运用程序Id,这个要跟服务器上的xml的applicationId一致,否则程序不会更新;
manifestUri表示服务器xml的地址
version表示当前版本,版本跟服务器上的不一致就更新
服务器端配置文件内容大概如下:
<?xml version="1.0" encoding="utf-8" ?>
<manifest>
<!--版本号,无格式要求,更新时需要修改-->
<version>1.2</version>
<description>更新说明,更新时需要修改</description>
<!--applicationId运用程序ID,无格式要求,需要与客户端配置一样,否则不会进行更新-->
<application applicationId="{215E1AD7-9ABA-432f-A952-24BABA556850}">
<!--重新启动exe名称,parameters启动时传入的参数-->
<entryPoint file="Client.exe" parameters="client" />
<!--更新文件的目录,相对于更新系统,默认为同级目录-->
<location>.</location>
</application>
<!--base表示存放该文件的url-->
<files base="http://localhost/update/">
<!--文件名称,更新时需要修改-->
<file source="test.txt" />
<file source="Client.exe" />
</files>
</manifest>
<manifest>
<!--版本号,无格式要求,更新时需要修改-->
<version>1.2</version>
<description>更新说明,更新时需要修改</description>
<!--applicationId运用程序ID,无格式要求,需要与客户端配置一样,否则不会进行更新-->
<application applicationId="{215E1AD7-9ABA-432f-A952-24BABA556850}">
<!--重新启动exe名称,parameters启动时传入的参数-->
<entryPoint file="Client.exe" parameters="client" />
<!--更新文件的目录,相对于更新系统,默认为同级目录-->
<location>.</location>
</application>
<!--base表示存放该文件的url-->
<files base="http://localhost/update/">
<!--文件名称,更新时需要修改-->
<file source="test.txt" />
<file source="Client.exe" />
</files>
</manifest>
XML对应的实体类
View Code
[XmlRoot("manifest")]
public class Manifest
{
[XmlElement("version")]
public string Version { get; set; }
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("fileBytes")]
public long FileBytes { get; set; }
[XmlElement("application")]
public Application Application { get; set; }
[XmlElement("files")]
public ManifestFiles ManifestFiles { get; set; }
}
public class ManifestFiles
{
[XmlElement("file")]
public ManifestFile[] Files { get; set; }
[XmlAttribute("base")]
public string BaseUrl { get; set; }
}
public class ManifestFile
{
[XmlAttribute("source")]
public string Source
{
get;
set;
}
[XmlAttribute("hash")]
public string Hash
{
get;
set;
}
}
public class Application
{
[XmlAttribute("applicationId")]
public string ApplicationId { get; set; }
[XmlElement("location")]
public string Location { get; set; }
[XmlElement("entryPoint")]
public EntryPoint EntryPoint { get; set; }
}
public class EntryPoint
{
[XmlAttribute("file")]
public string File { get; set; }
[XmlAttribute("parameters")]
public string Parameters { get; set; }
}
public class UpdaterConfigurationView
{
private static XmlDocument document = new XmlDocument();
private static readonly string xmlFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "updateconfiguration.config");
static UpdaterConfigurationView()
{
document.Load(xmlFileName);
}
public string Version
{
get
{
return document.SelectSingleNode("applicationUpdater").Attributes["version"].Value;
}
set
{
document.SelectSingleNode("applicationUpdater").Attributes["version"].Value = value;
document.Save(xmlFileName);
}
}
public string ApplicationId
{
get
{
return document.SelectSingleNode("applicationUpdater").Attributes["applicationId"].Value;
}
set
{
document.SelectSingleNode("applicationUpdater").Attributes["applicationId"].Value = value;
document.Save(xmlFileName);
}
}
public string ManifestUri
{
get
{
return document.SelectSingleNode("applicationUpdater").Attributes["manifestUri"].Value;
}
set
{
document.SelectSingleNode("applicationUpdater").Attributes["manifestUri"].Value = value;
document.Save(xmlFileName);
}
}
}
public class Manifest
{
[XmlElement("version")]
public string Version { get; set; }
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("fileBytes")]
public long FileBytes { get; set; }
[XmlElement("application")]
public Application Application { get; set; }
[XmlElement("files")]
public ManifestFiles ManifestFiles { get; set; }
}
public class ManifestFiles
{
[XmlElement("file")]
public ManifestFile[] Files { get; set; }
[XmlAttribute("base")]
public string BaseUrl { get; set; }
}
public class ManifestFile
{
[XmlAttribute("source")]
public string Source
{
get;
set;
}
[XmlAttribute("hash")]
public string Hash
{
get;
set;
}
}
public class Application
{
[XmlAttribute("applicationId")]
public string ApplicationId { get; set; }
[XmlElement("location")]
public string Location { get; set; }
[XmlElement("entryPoint")]
public EntryPoint EntryPoint { get; set; }
}
public class EntryPoint
{
[XmlAttribute("file")]
public string File { get; set; }
[XmlAttribute("parameters")]
public string Parameters { get; set; }
}
public class UpdaterConfigurationView
{
private static XmlDocument document = new XmlDocument();
private static readonly string xmlFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "updateconfiguration.config");
static UpdaterConfigurationView()
{
document.Load(xmlFileName);
}
public string Version
{
get
{
return document.SelectSingleNode("applicationUpdater").Attributes["version"].Value;
}
set
{
document.SelectSingleNode("applicationUpdater").Attributes["version"].Value = value;
document.Save(xmlFileName);
}
}
public string ApplicationId
{
get
{
return document.SelectSingleNode("applicationUpdater").Attributes["applicationId"].Value;
}
set
{
document.SelectSingleNode("applicationUpdater").Attributes["applicationId"].Value = value;
document.Save(xmlFileName);
}
}
public string ManifestUri
{
get
{
return document.SelectSingleNode("applicationUpdater").Attributes["manifestUri"].Value;
}
set
{
document.SelectSingleNode("applicationUpdater").Attributes["manifestUri"].Value = value;
document.Save(xmlFileName);
}
}
}
下载文件
下载文件是根据服务器端的配置文件进行的,配置文件设置了下载的文件路径,下载地址的url,下载文件保存的位置,由location节点设置,好了,不多说了,看下载的源码,这里我是写了一个可以异步下载的组件,里面用的是Ms自带的WebClient类,异步下载有事件通知,详见源码:
事件类
View Code
public class DownloadErrorEventArgs : EventArgs
{
public Exception Error { get; set; }
public Manifest Manifest { get; set; }
}
public class DownloadProgressEventArgs : ProgressChangedEventArgs
{
public DownloadProgressEventArgs(int progressPercentage, object userState)
: base(progressPercentage,userState)
{ }
/// <summary>
/// 当前下载的文件名
/// </summary>
public string FileName { get; set; }
/// <summary>
/// 获取收到的字节数。
/// </summary>
public long BytesReceived { get; set; }
/// <summary>
/// 获取 System.Net.WebClient 数据下载操作中的字节总数。
/// </summary>
public long TotalBytesToReceive { get; set; }
}
public class DownloadCompleteEventArgs : AsyncCompletedEventArgs
{
public DownloadCompleteEventArgs(Exception error, bool cancelled, object userState)
: base(error, cancelled, userState)
{
}
public Manifest Manifest { get; set; }
}
{
public Exception Error { get; set; }
public Manifest Manifest { get; set; }
}
public class DownloadProgressEventArgs : ProgressChangedEventArgs
{
public DownloadProgressEventArgs(int progressPercentage, object userState)
: base(progressPercentage,userState)
{ }
/// <summary>
/// 当前下载的文件名
/// </summary>
public string FileName { get; set; }
/// <summary>
/// 获取收到的字节数。
/// </summary>
public long BytesReceived { get; set; }
/// <summary>
/// 获取 System.Net.WebClient 数据下载操作中的字节总数。
/// </summary>
public long TotalBytesToReceive { get; set; }
}
public class DownloadCompleteEventArgs : AsyncCompletedEventArgs
{
public DownloadCompleteEventArgs(Exception error, bool cancelled, object userState)
: base(error, cancelled, userState)
{
}
public Manifest Manifest { get; set; }
}
下载代码
View Code
public class DownloadClass : Component
{
private WebClient webClient=new WebClient();
private Manifest manifest;
private int fileCount = 0;
private bool cancel = false;
private string tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "temp");
private HybridDictionary userStateToLifetime = new HybridDictionary();
private object defaultTaskId = new object();
private delegate void WorkerEventHandler(AsyncOperation asyncOp);
private System.ComponentModel.Container components = null;
private SendOrPostCallback onProgressReportDelegate;
private SendOrPostCallback onCompletedDelegate;
private AsyncOperation current;
/// <summary>
/// 下载进度
/// </summary>
public event EventHandler<DownloadProgressEventArgs> DownloadProgressChanged;
/// <summary>
/// 下载完成事件
/// </summary>
public event EventHandler<DownloadCompleteEventArgs> DownloadCompleted;
/// <summary>
/// 下载错误触发的事件
/// </summary>
public event EventHandler<DownloadErrorEventArgs> DownloadError;
public DownloadClass(IContainer container)
{
container.Add(this);
InitializeComponent();
InitializeDelegates();
}
public DownloadClass()
{
InitializeComponent();
InitializeDelegates();
}
/// <summary>
/// 初始化代理
/// </summary>
protected virtual void InitializeDelegates()
{
onProgressReportDelegate = new SendOrPostCallback(ReportProgress);
onCompletedDelegate = new SendOrPostCallback(DoDownloadCompleted);
}
/// <summary>
/// 触发下载进度事件
/// </summary>
/// <param name="e"></param>
protected virtual void OnDownloadProgressChanged(DownloadProgressEventArgs e)
{
if (DownloadProgressChanged != null)
{
DownloadProgressChanged(this, e);
}
}
/// <summary>
/// 触发下载完成事件
/// </summary>
/// <param name="e"></param>
protected virtual void OnDownloadCompleted(DownloadCompleteEventArgs e)
{
if (DownloadCompleted != null)
{
DownloadCompleted(this, e);
}
}
/// <summary>
/// 触发下载错误事件
/// </summary>
/// <param name="e"></param>
protected virtual void OnDownloadError(DownloadErrorEventArgs e)
{
if (DownloadError != null)
{
DownloadError(this, e);
}
}
/// <summary>
/// 下载文字保存的临时目录
/// </summary>
public string TempPath
{
get
{
return tempPath;
}
set
{
tempPath = value;
}
}
/// <summary>
/// 同步下载
/// </summary>
/// <param name="manifest"></param>
public void Download(Manifest manifest)
{
Init(manifest);
foreach (var file in manifest.ManifestFiles.Files)
{
string serverFileName = Path.Combine(manifest.ManifestFiles.BaseUrl, file.Source);
string clientFileName = Path.Combine(tempPath, file.Source);
Uri uri = new Uri(serverFileName);
if (!Directory.Exists(Path.GetDirectoryName(clientFileName)))
{
Directory.CreateDirectory(Path.GetDirectoryName(clientFileName));
}
webClient.DownloadFile(uri, clientFileName);
}
}
/// <summary>
/// 异步下载
/// </summary>
/// <param name="manifest"></param>
public void DownloadAsync(Manifest manifest)
{
Init(manifest);
DownloadAsync(manifest,defaultTaskId);
}
/// <summary>
/// 异步下载并指定任务Id
/// </summary>
/// <param name="manifest"></param>
/// <param name="taskId"></param>
public void DownloadAsync(Manifest manifest,object taskId)
{
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);
lock (userStateToLifetime.SyncRoot)
{
if (userStateToLifetime.Contains(taskId))
{
throw new ArgumentException("参数taskId必须是唯一的", "taskId");
}
userStateToLifetime[taskId] = asyncOp;
}
WorkerEventHandler workerDelegate = new WorkerEventHandler(DownloadWorker);
workerDelegate.BeginInvoke(asyncOp, null, null);
}
private void Init(Manifest manifest)
{
this.manifest = manifest;
webClient.BaseAddress = manifest.ManifestFiles.BaseUrl;
webClient.Credentials = CredentialCache.DefaultCredentials;
webClient.Encoding = Encoding.UTF8;
}
/// <summary>
/// 异步下载方法
/// </summary>
/// <param name="asyncOp"></param>
private void DownloadWorker(AsyncOperation asyncOp)
{
current = asyncOp;
if (!TaskCanceled(asyncOp.UserSuppliedState))
{
try
{
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(webClient_DownloadFileCompleted);
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
foreach (var file in manifest.ManifestFiles.Files)
{
string serverFileName = Path.Combine(manifest.ManifestFiles.BaseUrl, file.Source);
string clientFileName = Path.Combine(tempPath, file.Source);
Uri uri = new Uri(serverFileName);
if (!Directory.Exists(Path.GetDirectoryName(clientFileName)))
{
Directory.CreateDirectory(Path.GetDirectoryName(clientFileName));
}
while (webClient.IsBusy)
{
//阻塞异步下载
}
if (!cancel)
{
webClient.DownloadFileAsync(uri, clientFileName, file.Source);
}
}
}
catch (Exception ex)
{
DownloadErrorEventArgs e = new DownloadErrorEventArgs();
e.Error = ex;
e.Manifest = manifest;
OnDownloadError(e);
}
}
}
/// <summary>
/// 异步完成方法
/// </summary>
/// <param name="exception"></param>
/// <param name="canceled"></param>
/// <param name="asyncOp"></param>
private void CompletionMethod(Exception exception, bool canceled, AsyncOperation asyncOp)
{
if (!canceled)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(asyncOp.UserSuppliedState);
}
}
DownloadCompleteEventArgs e = new DownloadCompleteEventArgs(exception, canceled, asyncOp.UserSuppliedState);
e.Manifest = manifest;
asyncOp.PostOperationCompleted(onCompletedDelegate, e);
current = null;
}
/// <summary>
/// 异步下载进度事件(仅对于单个文件)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
DownloadProgressEventArgs args = new DownloadProgressEventArgs(e.ProgressPercentage, e.UserState);
args.BytesReceived = e.BytesReceived;
args.FileName = e.UserState.ToString();
args.TotalBytesToReceive = e.TotalBytesToReceive;
if (current != null)
{
current.Post(onProgressReportDelegate, args);
}
}
/// <summary>
/// 异步下载完成事件(仅对于单个文件)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
fileCount++;
if (fileCount == manifest.ManifestFiles.Files.Length)
{
this.CompletionMethod(e.Error, TaskCanceled(current.UserSuppliedState), current);
}
}
/// <summary>
/// 取消异步下载
/// </summary>
public void CancelAsync()
{
CancelAsync(defaultTaskId);
}
/// <summary>
/// 取消异步下载
/// </summary>
public void CancelAsync(object taskId)
{
webClient.CancelAsync();
cancel = true;
current = null;
AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation;
if (asyncOp != null)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(taskId);
}
}
}
private bool TaskCanceled(object taskId)
{
return cancel || (userStateToLifetime[taskId] == null);
}
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
private void DoDownloadCompleted(object operationState)
{
DownloadCompleteEventArgs e = operationState as DownloadCompleteEventArgs;
OnDownloadCompleted(e);
}
private void ReportProgress(object state)
{
DownloadProgressEventArgs e = state as DownloadProgressEventArgs;
OnDownloadProgressChanged(e);
}
}
{
private WebClient webClient=new WebClient();
private Manifest manifest;
private int fileCount = 0;
private bool cancel = false;
private string tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "temp");
private HybridDictionary userStateToLifetime = new HybridDictionary();
private object defaultTaskId = new object();
private delegate void WorkerEventHandler(AsyncOperation asyncOp);
private System.ComponentModel.Container components = null;
private SendOrPostCallback onProgressReportDelegate;
private SendOrPostCallback onCompletedDelegate;
private AsyncOperation current;
/// <summary>
/// 下载进度
/// </summary>
public event EventHandler<DownloadProgressEventArgs> DownloadProgressChanged;
/// <summary>
/// 下载完成事件
/// </summary>
public event EventHandler<DownloadCompleteEventArgs> DownloadCompleted;
/// <summary>
/// 下载错误触发的事件
/// </summary>
public event EventHandler<DownloadErrorEventArgs> DownloadError;
public DownloadClass(IContainer container)
{
container.Add(this);
InitializeComponent();
InitializeDelegates();
}
public DownloadClass()
{
InitializeComponent();
InitializeDelegates();
}
/// <summary>
/// 初始化代理
/// </summary>
protected virtual void InitializeDelegates()
{
onProgressReportDelegate = new SendOrPostCallback(ReportProgress);
onCompletedDelegate = new SendOrPostCallback(DoDownloadCompleted);
}
/// <summary>
/// 触发下载进度事件
/// </summary>
/// <param name="e"></param>
protected virtual void OnDownloadProgressChanged(DownloadProgressEventArgs e)
{
if (DownloadProgressChanged != null)
{
DownloadProgressChanged(this, e);
}
}
/// <summary>
/// 触发下载完成事件
/// </summary>
/// <param name="e"></param>
protected virtual void OnDownloadCompleted(DownloadCompleteEventArgs e)
{
if (DownloadCompleted != null)
{
DownloadCompleted(this, e);
}
}
/// <summary>
/// 触发下载错误事件
/// </summary>
/// <param name="e"></param>
protected virtual void OnDownloadError(DownloadErrorEventArgs e)
{
if (DownloadError != null)
{
DownloadError(this, e);
}
}
/// <summary>
/// 下载文字保存的临时目录
/// </summary>
public string TempPath
{
get
{
return tempPath;
}
set
{
tempPath = value;
}
}
/// <summary>
/// 同步下载
/// </summary>
/// <param name="manifest"></param>
public void Download(Manifest manifest)
{
Init(manifest);
foreach (var file in manifest.ManifestFiles.Files)
{
string serverFileName = Path.Combine(manifest.ManifestFiles.BaseUrl, file.Source);
string clientFileName = Path.Combine(tempPath, file.Source);
Uri uri = new Uri(serverFileName);
if (!Directory.Exists(Path.GetDirectoryName(clientFileName)))
{
Directory.CreateDirectory(Path.GetDirectoryName(clientFileName));
}
webClient.DownloadFile(uri, clientFileName);
}
}
/// <summary>
/// 异步下载
/// </summary>
/// <param name="manifest"></param>
public void DownloadAsync(Manifest manifest)
{
Init(manifest);
DownloadAsync(manifest,defaultTaskId);
}
/// <summary>
/// 异步下载并指定任务Id
/// </summary>
/// <param name="manifest"></param>
/// <param name="taskId"></param>
public void DownloadAsync(Manifest manifest,object taskId)
{
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);
lock (userStateToLifetime.SyncRoot)
{
if (userStateToLifetime.Contains(taskId))
{
throw new ArgumentException("参数taskId必须是唯一的", "taskId");
}
userStateToLifetime[taskId] = asyncOp;
}
WorkerEventHandler workerDelegate = new WorkerEventHandler(DownloadWorker);
workerDelegate.BeginInvoke(asyncOp, null, null);
}
private void Init(Manifest manifest)
{
this.manifest = manifest;
webClient.BaseAddress = manifest.ManifestFiles.BaseUrl;
webClient.Credentials = CredentialCache.DefaultCredentials;
webClient.Encoding = Encoding.UTF8;
}
/// <summary>
/// 异步下载方法
/// </summary>
/// <param name="asyncOp"></param>
private void DownloadWorker(AsyncOperation asyncOp)
{
current = asyncOp;
if (!TaskCanceled(asyncOp.UserSuppliedState))
{
try
{
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(webClient_DownloadFileCompleted);
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
foreach (var file in manifest.ManifestFiles.Files)
{
string serverFileName = Path.Combine(manifest.ManifestFiles.BaseUrl, file.Source);
string clientFileName = Path.Combine(tempPath, file.Source);
Uri uri = new Uri(serverFileName);
if (!Directory.Exists(Path.GetDirectoryName(clientFileName)))
{
Directory.CreateDirectory(Path.GetDirectoryName(clientFileName));
}
while (webClient.IsBusy)
{
//阻塞异步下载
}
if (!cancel)
{
webClient.DownloadFileAsync(uri, clientFileName, file.Source);
}
}
}
catch (Exception ex)
{
DownloadErrorEventArgs e = new DownloadErrorEventArgs();
e.Error = ex;
e.Manifest = manifest;
OnDownloadError(e);
}
}
}
/// <summary>
/// 异步完成方法
/// </summary>
/// <param name="exception"></param>
/// <param name="canceled"></param>
/// <param name="asyncOp"></param>
private void CompletionMethod(Exception exception, bool canceled, AsyncOperation asyncOp)
{
if (!canceled)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(asyncOp.UserSuppliedState);
}
}
DownloadCompleteEventArgs e = new DownloadCompleteEventArgs(exception, canceled, asyncOp.UserSuppliedState);
e.Manifest = manifest;
asyncOp.PostOperationCompleted(onCompletedDelegate, e);
current = null;
}
/// <summary>
/// 异步下载进度事件(仅对于单个文件)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
DownloadProgressEventArgs args = new DownloadProgressEventArgs(e.ProgressPercentage, e.UserState);
args.BytesReceived = e.BytesReceived;
args.FileName = e.UserState.ToString();
args.TotalBytesToReceive = e.TotalBytesToReceive;
if (current != null)
{
current.Post(onProgressReportDelegate, args);
}
}
/// <summary>
/// 异步下载完成事件(仅对于单个文件)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
fileCount++;
if (fileCount == manifest.ManifestFiles.Files.Length)
{
this.CompletionMethod(e.Error, TaskCanceled(current.UserSuppliedState), current);
}
}
/// <summary>
/// 取消异步下载
/// </summary>
public void CancelAsync()
{
CancelAsync(defaultTaskId);
}
/// <summary>
/// 取消异步下载
/// </summary>
public void CancelAsync(object taskId)
{
webClient.CancelAsync();
cancel = true;
current = null;
AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation;
if (asyncOp != null)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(taskId);
}
}
}
private bool TaskCanceled(object taskId)
{
return cancel || (userStateToLifetime[taskId] == null);
}
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
private void DoDownloadCompleted(object operationState)
{
DownloadCompleteEventArgs e = operationState as DownloadCompleteEventArgs;
OnDownloadCompleted(e);
}
private void ReportProgress(object state)
{
DownloadProgressEventArgs e = state as DownloadProgressEventArgs;
OnDownloadProgressChanged(e);
}
}
更新文件
也就是把下载的文件覆盖到客户端安装的目录,代码如下:
事件参数类
View Code
/// <summary>
/// 文件复制进度报告事件参数
/// </summary>
public class FileCopyProgressChangedEventArgs : ProgressChangedEventArgs
{
public FileCopyProgressChangedEventArgs(int progressPercentage, object userState)
: base(progressPercentage, userState)
{
}
/// <summary>
/// 当前复制的字节数
/// </summary>
public double BytesToCopy { get; set; }
/// <summary>
/// 当前复制操作中的字节总数
/// </summary>
public double TotalBytesToCopy { get; set; }
/// <summary>
/// 当前复制的源文件名
/// </summary>
public string SourceFileName { get; set; }
/// <summary>
/// 当前复制的目标文件名
/// </summary>
public string TargetFileName { get; set; }
public Manifest Manifest { get; set; }
}
/// <summary>
/// 文件复制完成事件参数
/// </summary>
public class FileCopyCompletedEventArgs : AsyncCompletedEventArgs
{
public FileCopyCompletedEventArgs(Exception error, bool cancelled, object userState)
: base(error, cancelled, userState)
{
}
public Manifest Manifest { get; set; }
}
public class FileCopyErrorEventArgs : EventArgs
{
public Exception Error { get; set; }
public Manifest Manifest { get; set; }
}
/// 文件复制进度报告事件参数
/// </summary>
public class FileCopyProgressChangedEventArgs : ProgressChangedEventArgs
{
public FileCopyProgressChangedEventArgs(int progressPercentage, object userState)
: base(progressPercentage, userState)
{
}
/// <summary>
/// 当前复制的字节数
/// </summary>
public double BytesToCopy { get; set; }
/// <summary>
/// 当前复制操作中的字节总数
/// </summary>
public double TotalBytesToCopy { get; set; }
/// <summary>
/// 当前复制的源文件名
/// </summary>
public string SourceFileName { get; set; }
/// <summary>
/// 当前复制的目标文件名
/// </summary>
public string TargetFileName { get; set; }
public Manifest Manifest { get; set; }
}
/// <summary>
/// 文件复制完成事件参数
/// </summary>
public class FileCopyCompletedEventArgs : AsyncCompletedEventArgs
{
public FileCopyCompletedEventArgs(Exception error, bool cancelled, object userState)
: base(error, cancelled, userState)
{
}
public Manifest Manifest { get; set; }
}
public class FileCopyErrorEventArgs : EventArgs
{
public Exception Error { get; set; }
public Manifest Manifest { get; set; }
}
文件复制类,可以使用异步复制,异步复制提供了事件通知的机制,即可以有进度报告,文件复制也是一个组件类
View Code
/// <summary>
/// 文件复制组件类
/// </summary>
public class FileCopyClass : Component
{
private object defaultTaskId = new object();
private int writeFileLength = 1024 * 64;
private delegate void WorkerEventHandler(Manifest manifest, string sourcePath, AsyncOperation asyncOp);
private SendOrPostCallback onProgressReportDelegate;
private SendOrPostCallback onCompletedDelegate;
private HybridDictionary userStateToLifetime = new HybridDictionary();
private System.ComponentModel.Container components = null;
#region Public events
/// <summary>
/// 文件复制进度事件
/// </summary>
public event EventHandler<FileCopyProgressChangedEventArgs> FileCopyProgressChanged;
/// <summary>
/// 文件复制完成事件
/// </summary>
public event EventHandler<FileCopyCompletedEventArgs> FileCopyCompleted;
public event EventHandler<FileCopyErrorEventArgs> FileCopyError;
#endregion
#region Construction and destruction
public FileCopyClass(IContainer container)
{
container.Add(this);
InitializeComponent();
InitializeDelegates();
}
public FileCopyClass()
{
InitializeComponent();
InitializeDelegates();
}
protected virtual void InitializeDelegates()
{
onProgressReportDelegate = new SendOrPostCallback(ReportProgress);
onCompletedDelegate = new SendOrPostCallback(CopyCompleted);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
#endregion
#region 实现
public int WriteFileLength
{
set
{
writeFileLength = value;
}
}
public void Copy(Manifest manifest, string sourcePath)
{
string[] sourceFiles = null;
string[] targetFiles = null;
GetFiles(manifest, sourcePath, out sourceFiles, out targetFiles);
for (int i = 0; i < sourceFiles.Length; i++)
{
if (!Directory.Exists(Path.GetDirectoryName(targetFiles[i])))
{
Directory.CreateDirectory(Path.GetDirectoryName(targetFiles[i]));
}
File.Copy(sourceFiles[i], targetFiles[i], true);
}
}
public void CopyAsync(Manifest manifest, string sourcePath)
{
CopyAsync(manifest, sourcePath, defaultTaskId);
}
public void CopyAsync(Manifest manifest, string sourcePath, object taskId)
{
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);
lock (userStateToLifetime.SyncRoot)
{
if (userStateToLifetime.Contains(taskId))
{
throw new ArgumentException("参数taskId必须是唯一的", "taskId");
}
userStateToLifetime[taskId] = asyncOp;
}
WorkerEventHandler workerDelegate = new WorkerEventHandler(FileCopyWorker);
workerDelegate.BeginInvoke(manifest, sourcePath, asyncOp, null, null);
}
private bool TaskCanceled(object taskId)
{
return (userStateToLifetime[taskId] == null);
}
public void CancelAsync()
{
CancelAsync(defaultTaskId);
}
public void CancelAsync(object taskId)
{
AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation;
if (asyncOp != null)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(taskId);
}
}
}
private void FileCopyWorker(Manifest manifest, string sourcePath, AsyncOperation asyncOp)
{
Exception exception = null;
FileCopyProgressChangedEventArgs e = null;
Stream rStream = null;
Stream wStream = null;
double writeBytes = 0;
string[] sourceFiles = null;
string[] targetFiles = null;
GetFiles(manifest, sourcePath,out sourceFiles,out targetFiles);
if (!TaskCanceled(asyncOp.UserSuppliedState))
{
try
{
double totalBytes = GetFileLength(sourceFiles);
byte[] buffer = new byte[writeFileLength];
int len = 0;
int offset = 0;
for (int i = 0; i < sourceFiles.Length; i++)
{
try
{
if (!Directory.Exists(Path.GetDirectoryName(targetFiles[i])))
{
Directory.CreateDirectory(Path.GetDirectoryName(targetFiles[i]));
}
rStream = new FileStream(sourceFiles[i], FileMode.Open, FileAccess.Read, FileShare.None);
wStream = new FileStream(targetFiles[i], FileMode.Create, FileAccess.Write, FileShare.None);
while ((len = rStream.Read(buffer, offset, writeFileLength)) > 0)
{
wStream.Write(buffer, offset, len);
writeBytes += len;
e = new FileCopyProgressChangedEventArgs((int)(writeBytes / totalBytes * 100), asyncOp.UserSuppliedState);
e.SourceFileName = sourceFiles[i];
e.TargetFileName = targetFiles[i];
e.TotalBytesToCopy = totalBytes;
e.BytesToCopy = len;
e.Manifest = manifest;
asyncOp.Post(this.onProgressReportDelegate, e);
Thread.Sleep(1);
}
}
finally
{
DisposeStream(wStream);
DisposeStream(rStream);
}
}
}
catch (Exception ex)
{
exception = ex;
OnFileCopyError(new FileCopyErrorEventArgs() { Error = ex, Manifest = manifest });
}
}
this.CompletionMethod(manifest, exception, TaskCanceled(asyncOp.UserSuppliedState), asyncOp);
}
private void GetFiles(Manifest manifest, string sourcePath,out string[] sourceFiles,out string[] targetFiles)
{
sourceFiles = new string[manifest.ManifestFiles.Files.Length];
targetFiles = new string[manifest.ManifestFiles.Files.Length];
string path = Path.GetFullPath(manifest.Application.Location);
for (int i = 0; i < manifest.ManifestFiles.Files.Length; i++)
{
sourceFiles[i] = Path.Combine(sourcePath, manifest.ManifestFiles.Files[i].Source);
targetFiles[i] = Path.Combine(path, manifest.ManifestFiles.Files[i].Source);
}
}
private void DisposeStream(Stream stream)
{
if (stream != null)
{
stream.Flush();
stream.Close();
stream.Dispose();
}
}
private double GetFileLength(string[] sourceFiles)
{
double bytes = 0;
foreach (var file in sourceFiles)
{
FileInfo fileInfo = new FileInfo(file);
bytes += fileInfo.Length;
}
return bytes;
}
private void CopyCompleted(object operationState)
{
FileCopyCompletedEventArgs e = operationState as FileCopyCompletedEventArgs;
OnFileCopyCompleted(e);
}
private void ReportProgress(object state)
{
FileCopyProgressChangedEventArgs e = state as FileCopyProgressChangedEventArgs;
OnProgressChanged(e);
}
protected void OnFileCopyCompleted(FileCopyCompletedEventArgs e)
{
if (FileCopyCompleted != null)
{
FileCopyCompleted(this, e);
}
}
protected void OnProgressChanged(FileCopyProgressChangedEventArgs e)
{
if (FileCopyProgressChanged != null)
{
FileCopyProgressChanged(this, e);
}
}
protected void OnFileCopyError(FileCopyErrorEventArgs e)
{
if (FileCopyError != null)
{
FileCopyError(this, e);
}
}
private void CompletionMethod(Manifest manifest, Exception exception, bool canceled, AsyncOperation asyncOp)
{
if (!canceled)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(asyncOp.UserSuppliedState);
}
}
FileCopyCompletedEventArgs e = new FileCopyCompletedEventArgs(exception, canceled, asyncOp.UserSuppliedState);
e.Manifest = manifest;
asyncOp.PostOperationCompleted(onCompletedDelegate, e);
}
#endregion
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
}
/// 文件复制组件类
/// </summary>
public class FileCopyClass : Component
{
private object defaultTaskId = new object();
private int writeFileLength = 1024 * 64;
private delegate void WorkerEventHandler(Manifest manifest, string sourcePath, AsyncOperation asyncOp);
private SendOrPostCallback onProgressReportDelegate;
private SendOrPostCallback onCompletedDelegate;
private HybridDictionary userStateToLifetime = new HybridDictionary();
private System.ComponentModel.Container components = null;
#region Public events
/// <summary>
/// 文件复制进度事件
/// </summary>
public event EventHandler<FileCopyProgressChangedEventArgs> FileCopyProgressChanged;
/// <summary>
/// 文件复制完成事件
/// </summary>
public event EventHandler<FileCopyCompletedEventArgs> FileCopyCompleted;
public event EventHandler<FileCopyErrorEventArgs> FileCopyError;
#endregion
#region Construction and destruction
public FileCopyClass(IContainer container)
{
container.Add(this);
InitializeComponent();
InitializeDelegates();
}
public FileCopyClass()
{
InitializeComponent();
InitializeDelegates();
}
protected virtual void InitializeDelegates()
{
onProgressReportDelegate = new SendOrPostCallback(ReportProgress);
onCompletedDelegate = new SendOrPostCallback(CopyCompleted);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
#endregion
#region 实现
public int WriteFileLength
{
set
{
writeFileLength = value;
}
}
public void Copy(Manifest manifest, string sourcePath)
{
string[] sourceFiles = null;
string[] targetFiles = null;
GetFiles(manifest, sourcePath, out sourceFiles, out targetFiles);
for (int i = 0; i < sourceFiles.Length; i++)
{
if (!Directory.Exists(Path.GetDirectoryName(targetFiles[i])))
{
Directory.CreateDirectory(Path.GetDirectoryName(targetFiles[i]));
}
File.Copy(sourceFiles[i], targetFiles[i], true);
}
}
public void CopyAsync(Manifest manifest, string sourcePath)
{
CopyAsync(manifest, sourcePath, defaultTaskId);
}
public void CopyAsync(Manifest manifest, string sourcePath, object taskId)
{
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);
lock (userStateToLifetime.SyncRoot)
{
if (userStateToLifetime.Contains(taskId))
{
throw new ArgumentException("参数taskId必须是唯一的", "taskId");
}
userStateToLifetime[taskId] = asyncOp;
}
WorkerEventHandler workerDelegate = new WorkerEventHandler(FileCopyWorker);
workerDelegate.BeginInvoke(manifest, sourcePath, asyncOp, null, null);
}
private bool TaskCanceled(object taskId)
{
return (userStateToLifetime[taskId] == null);
}
public void CancelAsync()
{
CancelAsync(defaultTaskId);
}
public void CancelAsync(object taskId)
{
AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation;
if (asyncOp != null)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(taskId);
}
}
}
private void FileCopyWorker(Manifest manifest, string sourcePath, AsyncOperation asyncOp)
{
Exception exception = null;
FileCopyProgressChangedEventArgs e = null;
Stream rStream = null;
Stream wStream = null;
double writeBytes = 0;
string[] sourceFiles = null;
string[] targetFiles = null;
GetFiles(manifest, sourcePath,out sourceFiles,out targetFiles);
if (!TaskCanceled(asyncOp.UserSuppliedState))
{
try
{
double totalBytes = GetFileLength(sourceFiles);
byte[] buffer = new byte[writeFileLength];
int len = 0;
int offset = 0;
for (int i = 0; i < sourceFiles.Length; i++)
{
try
{
if (!Directory.Exists(Path.GetDirectoryName(targetFiles[i])))
{
Directory.CreateDirectory(Path.GetDirectoryName(targetFiles[i]));
}
rStream = new FileStream(sourceFiles[i], FileMode.Open, FileAccess.Read, FileShare.None);
wStream = new FileStream(targetFiles[i], FileMode.Create, FileAccess.Write, FileShare.None);
while ((len = rStream.Read(buffer, offset, writeFileLength)) > 0)
{
wStream.Write(buffer, offset, len);
writeBytes += len;
e = new FileCopyProgressChangedEventArgs((int)(writeBytes / totalBytes * 100), asyncOp.UserSuppliedState);
e.SourceFileName = sourceFiles[i];
e.TargetFileName = targetFiles[i];
e.TotalBytesToCopy = totalBytes;
e.BytesToCopy = len;
e.Manifest = manifest;
asyncOp.Post(this.onProgressReportDelegate, e);
Thread.Sleep(1);
}
}
finally
{
DisposeStream(wStream);
DisposeStream(rStream);
}
}
}
catch (Exception ex)
{
exception = ex;
OnFileCopyError(new FileCopyErrorEventArgs() { Error = ex, Manifest = manifest });
}
}
this.CompletionMethod(manifest, exception, TaskCanceled(asyncOp.UserSuppliedState), asyncOp);
}
private void GetFiles(Manifest manifest, string sourcePath,out string[] sourceFiles,out string[] targetFiles)
{
sourceFiles = new string[manifest.ManifestFiles.Files.Length];
targetFiles = new string[manifest.ManifestFiles.Files.Length];
string path = Path.GetFullPath(manifest.Application.Location);
for (int i = 0; i < manifest.ManifestFiles.Files.Length; i++)
{
sourceFiles[i] = Path.Combine(sourcePath, manifest.ManifestFiles.Files[i].Source);
targetFiles[i] = Path.Combine(path, manifest.ManifestFiles.Files[i].Source);
}
}
private void DisposeStream(Stream stream)
{
if (stream != null)
{
stream.Flush();
stream.Close();
stream.Dispose();
}
}
private double GetFileLength(string[] sourceFiles)
{
double bytes = 0;
foreach (var file in sourceFiles)
{
FileInfo fileInfo = new FileInfo(file);
bytes += fileInfo.Length;
}
return bytes;
}
private void CopyCompleted(object operationState)
{
FileCopyCompletedEventArgs e = operationState as FileCopyCompletedEventArgs;
OnFileCopyCompleted(e);
}
private void ReportProgress(object state)
{
FileCopyProgressChangedEventArgs e = state as FileCopyProgressChangedEventArgs;
OnProgressChanged(e);
}
protected void OnFileCopyCompleted(FileCopyCompletedEventArgs e)
{
if (FileCopyCompleted != null)
{
FileCopyCompleted(this, e);
}
}
protected void OnProgressChanged(FileCopyProgressChangedEventArgs e)
{
if (FileCopyProgressChanged != null)
{
FileCopyProgressChanged(this, e);
}
}
protected void OnFileCopyError(FileCopyErrorEventArgs e)
{
if (FileCopyError != null)
{
FileCopyError(this, e);
}
}
private void CompletionMethod(Manifest manifest, Exception exception, bool canceled, AsyncOperation asyncOp)
{
if (!canceled)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(asyncOp.UserSuppliedState);
}
}
FileCopyCompletedEventArgs e = new FileCopyCompletedEventArgs(exception, canceled, asyncOp.UserSuppliedState);
e.Manifest = manifest;
asyncOp.PostOperationCompleted(onCompletedDelegate, e);
}
#endregion
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
}
好了,基本的代码已经完成了,其他都是客户端调用的代码,客户端有2个exe程序,一个是主程序,另外一个是更新程序,所以使用2个exe的原因是,主程序在打开的情况是无法进行更新的,就是文件不能被覆盖,那么主程序只要引用上面写的程序的dll,以及一个配置文件,配置文件的名称在程序写死了的,默认是在exe目录,名称为updateconfiguration.config,主程序只要调用UpdateClass类的CheckForUpdates方法就可以了
下面说下整体的部署方式
服务器:一个配置文件,记录更新的文件,记录的文件目录一定是存在的,否则会出现404错误
客户端:主程序+更新程序+配置文件,即把更新程序编译后的exe打包到主程序中,
以上程序进行了简单的测试,如果您下载使用了出现了问题,请及时联系我。