惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
K
Kaspersky official blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
The Exploit Database - CXSecurity.com
Cisco Talos Blog
Cisco Talos Blog
P
Palo Alto Networks Blog
Latest news
Latest news
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Privacy International News Feed
The Hacker News
The Hacker News
T
Tor Project blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
Cisco Blogs
阮一峰的网络日志
阮一峰的网络日志
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园_首页
N
News and Events Feed by Topic
W
WeLiveSecurity
罗磊的独立博客
GbyAI
GbyAI
T
Troy Hunt's Blog
Y
Y Combinator Blog
Recorded Future
Recorded Future
The Cloudflare Blog
TaoSecurity Blog
TaoSecurity Blog
爱范儿
爱范儿
美团技术团队
Attack and Defense Labs
Attack and Defense Labs
C
Check Point Blog
Engineering at Meta
Engineering at Meta
Cyberwarzone
Cyberwarzone
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
F
Fortinet All Blogs
The GitHub Blog
The GitHub Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Apple Machine Learning Research
Apple Machine Learning Research
Know Your Adversary
Know Your Adversary
AWS News Blog
AWS News Blog
D
DataBreaches.Net
Recent Announcements
Recent Announcements
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
M
MIT News - Artificial intelligence
Webroot Blog
Webroot Blog
Security Latest
Security Latest
T
Tailwind CSS Blog
V2EX - 技术
V2EX - 技术
aimingoo的专栏
aimingoo的专栏
S
Security @ Cisco Blogs
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed

博客园 - 风也无奈

事件学习 信息系统需求分析阶段的实践经验之二---如何有效地获得用户需求【转】 信息系统需求分析阶段的实践经验之一---需求分析概述[转] Lambda表达式【转】 C#委托的介绍(delegate、Action、Func、predicate)【转】 “DllRegisterServer的调用失败”问题解决办法 什么是RFID技术 SQL Server集群服务器的优缺点 asp中的ckEditor的详细配置 UTF8转成GB2312乱码问题解决思路 C#开发串口总结,并提炼串口辅助类到公用类库中 SQLXML]FOR XML语法导出XML的易错之处 C#下载功能代码<转> C#通用类库--QQ吸附窗体类 C#通用类库--数字转为人民币汉字大写表示 - 风也无奈 - 博客园 link和@import的区别 - 风也无奈 - 博客园 C#操作word并格式化 Winform中的DataGridView控件内容自动保存《转载》 SQL中获取一个长字符串中某个字符串出现次数的简单方法
winform更新程序代码
风也无奈 · 2011-07-20 · via 博客园 - 风也无奈

这是我的第一个随笔,写的不好的或者语法错误等,请大家不要见怪,首先预览下截图

 

自动更新一般分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>

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>

XML对应的实体类

View Code

[XmlRoot("manifest")]
    
public class Manifest
    {
        [XmlElement(
"version")]
        
public string Version { getset; }

        [XmlElement(

"description")]
        
public string Description { getset; }

        [XmlElement(

"fileBytes")]
        
public long FileBytes { getset; }

        [XmlElement(

"application")]
        
public Application Application { getset; }

        [XmlElement(

"files")]
        
public ManifestFiles ManifestFiles { getset; }
    }
public class ManifestFiles
    {
        [XmlElement(
"file")]
        
public ManifestFile[] Files { getset; }

        [XmlAttribute(

"base")]
        
public string BaseUrl { getset; }
    }
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 { getset; }

        [XmlElement(

"location")]
        
public string Location { getset; }

        [XmlElement(

"entryPoint")]
        
public EntryPoint EntryPoint { getset; }
    }
public class EntryPoint
    {
        [XmlAttribute(
"file")]
        
public string File { getset; }

        [XmlAttribute(

"parameters")]
        
public string Parameters { getset; }
    }
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 { getset; }public Manifest Manifest { getset; }
    }
public class DownloadProgressEventArgs : ProgressChangedEventArgs
    {
        
public DownloadProgressEventArgs(int progressPercentage, object userState)
            : 
base(progressPercentage,userState)
        { }
/// <summary>
        
/// 当前下载的文件名
        
/// </summary>
        public string FileName { getset; }/// <summary>
        
/// 获取收到的字节数。
        
/// </summary>
        public long BytesReceived { getset; }
        
/// <summary>
        
/// 获取 System.Net.WebClient 数据下载操作中的字节总数。
        
/// </summary>
        public long TotalBytesToReceive { getset; }
    }
public class DownloadCompleteEventArgs : AsyncCompletedEventArgs
    {
        
public DownloadCompleteEventArgs(Exception error, bool cancelled, object userState)
            : 
base(error, cancelled, userState)
        { 
        }
public Manifest Manifest { getset; }
    }

 下载代码

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, 
nullnull);
        }
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 { getset; }/// <summary>
        
/// 当前复制操作中的字节总数
        
/// </summary>
        public double TotalBytesToCopy { getset; }/// <summary>
        
/// 当前复制的源文件名
        
/// </summary>
        public string SourceFileName { getset; }/// <summary>
        
/// 当前复制的目标文件名
        
/// </summary>
        public string TargetFileName { getset; }public Manifest Manifest { getset; }
    }
/// <summary>
    
/// 文件复制完成事件参数
    
/// </summary>
    public class FileCopyCompletedEventArgs : AsyncCompletedEventArgs
    {
        
public FileCopyCompletedEventArgs(Exception error, bool cancelled, object userState)
            : 
base(error, cancelled, userState)
        {
        }
public Manifest Manifest { getset; }
    }
public class FileCopyErrorEventArgs : EventArgs
    {
        
public Exception Error { getset; }public Manifest Manifest { getset; }
    }

文件复制类,可以使用异步复制,异步复制提供了事件通知的机制,即可以有进度报告,文件复制也是一个组件类

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 destructionpublic 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, 
nullnull);
        }
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);
        }
#endregionprivate void InitializeComponent()
        {
            components 
= new System.ComponentModel.Container();
        }
    }

 好了,基本的代码已经完成了,其他都是客户端调用的代码,客户端有2个exe程序,一个是主程序,另外一个是更新程序,所以使用2个exe的原因是,主程序在打开的情况是无法进行更新的,就是文件不能被覆盖,那么主程序只要引用上面写的程序的dll,以及一个配置文件,配置文件的名称在程序写死了的,默认是在exe目录,名称为updateconfiguration.config,主程序只要调用UpdateClass类的CheckForUpdates方法就可以了

Demo

下面说下整体的部署方式

服务器:一个配置文件,记录更新的文件,记录的文件目录一定是存在的,否则会出现404错误

客户端:主程序+更新程序+配置文件,即把更新程序编译后的exe打包到主程序中,

以上程序进行了简单的测试,如果您下载使用了出现了问题,请及时联系我。