写自己的自动升级模块

6-30更新内容:

        由于编写代码的时候没有考虑到更新文件存放的真实位置,所以可能导致软件目录下其他文件夹文件不能更新,代码稍作修改:

        更新配置文件中用相对路径表示文件更新后的保存位置,如:

       <file name="doc\1.rar"  src="http://localhost/1.rar" version="3" size="1394007" isrestart="false" />

       程序中需要判断是否存在软件目录下的doc目录,并且在临时更新文件夹update中创建临时更新二级目录防止不同目录下有相同文件名的文件

       关键位置代码修改如下: 

 

代码
/*相对路径标示文件保存位置*/
                    
string tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "update", file.Name);
                    
if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
                        Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
                    
string truePath=Path.GetDirectoryName(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,file.Name));
                    
if (!Directory.Exists(truePath))
                        Directory.CreateDirectory(truePath);
                    client.DownloadFileAsync(
new Uri(file.DownloadUrl), tempPath);

 

 

       还有一处,由于没有充分考虑本地更新列表的自身更新问题,可能导致保存时没有保存好不需要更新的文件信息在列表中,所以需要修改2处代码

      在判断本地与远程列表时,将不需要更新的文件信息添加到afterCheckUpdate对象中,代码如下

 

代码
if(file.Version!=rf.Version) //如果版本不同
                            {
                                downloadList.Add(
new DownloadFileInfo(rf.Name, rf.Src,file.Version, rf.Version, rf.Size));
                            }
                            
else
                            {
                                afterCheckUpdate.Add(file); 
//保存不需要更新的文件
                            }
=======================================
检查完后

 config.UpdateFileList 
= afterCheckUpdate; //保留无需更新的文件

=========================================
将更新窗体中下句注释掉

 
//config.UpdateFileList.Clear(); //先清除本地列表,从更新列表中更新成功的项添加到本地列表

 

 

      另外为了防止本地更新文件丢失,可以在资源文件中添加一个空的本地更新文件,修改代码如下:

      if (!File.Exists(file))

            {

                using(StreamWriter sw=new StreamWriter(file))

                {

                    sw.Write(Properties.Resource.update);

                }

                throw new ArgumentException("更新配置文件丢失!请重启软件进行修复!");

            } 

       这样,本地文件丢失后,自动释放一个空的本地文件,重启软件的时候就会按照远程更新文件重新更新一次软件!

       资源文件中的 update就是本地 update.config ,可以做为项目文件,在引用项目是修改下update.config中的

       <ServerUrl>http://localhost/update.xml</ServerUrl>  

       就好了,

 

 

      重新上传更新后的完整项目,在文章最后下载! 

 

 

经常写一些winform的程序,大多也是为他人定制,但由于懒惰一直没有做的比较完善,

比如:1,没有做完善的注册验证

        2,没有自动更新

现在有时间了,又参照了一些自动升级程序的博文,写了一份自己的升级端,发布出来与大家同学习进步!

 

首先看下外表:

 

 

 

可以看到升级程序没有取消更新按钮以及ControlBox,主要考虑是我实际开发的程序中,必须自动检测更新,且必须执行更新!

 

 所以在Update.dll 模块设计中只提供一个 StartUpdate() 接口! 

 

 下面来看程序:

首先是更新配置文件,这2个文件按照网上其他人的自动升级程序的配置文件修改的,所以网络给了我们一个很好的学习空间


 

本地更新文件update.config
<?xml version="1.0" encoding="utf-8"?>
<Config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  
<ServerUrl>http://localhost/update.xml</ServerUrl>
  <UpdateFileList>
    
<LocalFile name="doc\1.rar" version="3" size="1394007" />
    
<LocalFile name="doc\2.rar" version="3" size="1394007" />
    
<LocalFile name="doc\3.rar" version="3" size="1191936" />
    
<LocalFile name="doc\4.rar" version="3" size="1191936" />
    
<LocalFile name="Tester.exe" version="3" size="14336" />
  
</UpdateFileList>
</Config>

 

 

远程更新文件update.xml
<?xml version="1.0" encoding="utf-8"?>
<updateFiles>
    
<file name="doc\1.rar"  src="http://localhost/1.rar" version="3" size="1394007" isrestart="false" />
    
<file name="doc\2.rar"  src="http://localhost/2.rar" version="3" size="1394007" isrestart="false" />
    
<file name="doc\3.rar"  src="http://localhost/3.rar" version="3" size="1191936" isrestart="false" />
    
<file name="doc\4.rar"  src="http://localhost/4.rar" version="3" size="1191936" isrestart="false" />
    
<file name="Tester.exe"  src="http://localhost/Tester.exe" version="3" size="14336" isrestart="false" />
</updateFiles>

 

 

 大家都升级程序的原理就是读取远程更新文件,与本地更新文件进行对比,有更新信息则下载更新!

 

 接着是进行比对的核心代码

Update()
/// <summary>
        
/// 开始更新
        
/// </summary>
        public void Update()
        {
            
using(WebClient client = new WebClient())
            {
                
string strXml = client.DownloadString(config.ServerUrl);
                Dictionary<string, RemoteFile> listRemotFile = ParseRemoteXml(strXml);
                List<DownloadFileInfo> downloadList = new List<DownloadFileInfo>();//下载列表
                foreach (string fileName in listRemotFile.Keys)
                {
                    RemoteFile rf=listRemotFile[fileName];
                    
bool bfind = false;
                    
for (int i = 0; i < config.UpdateFileList.Count; i++)
                    {
                        LocalFile file = config.UpdateFileList[i];
                        
if (file.Name == fileName)
                        {
                            bfind = true;
                            
if(file.Version!=rf.Version) //如果版本不同
                            {
                                downloadList.Add(new DownloadFileInfo(rf.Name, rf.Src,file.Version, rf.Version, rf.Size));
                            }
                            
break//发现相同文件就退出
                        }
                        
else //如果本地列表中没有远程列表中的文件
                        {
                            bfind = false;
                        }

                    }
                    
if(!bfind)//远程文件不存在本地列表中
                        downloadList.Add(new DownloadFileInfo(rf.Name, rf.Src, rf.Version, rf.Version,rf.Size));
                }
                
if(downloadList.Count>0)
                {
                    
                    DownloadForm frm = new DownloadForm(downloadList,config);
                    frm.ShowDialog();
                }
            }
        }

 

 

其中RemoteFile,LocalFile,DownloadFinle 是分别定义的远程更新文件对象,本地文件对象,和下载文件对象

 

RemoteFile.cs
 class RemoteFile
    {
        
public RemoteFile(XmlNode node)
        {
            
this.Name = node.Attributes["name"].Value;
            
this.Src = node.Attributes["src"].Value;
            
this.Version = node.Attributes["version"].Value;
            
this.Size = Convert.ToInt64(node.Attributes["size"].Value);
            
this.IsRestart = Convert.ToBoolean(node.Attributes["isrestart"].Value);
        }
        
public string Name{get;set;}
        
public string Src{get;set;}
        
public string Version{get;set;}
        
public long Size { getset; }
        
public bool IsRestart{get;set;}
    }

 

 

LocalFile.cs
public class LocalFile
    {
        
public LocalFile()
        {

        }
        
public LocalFile(string name, string version, long size)
        {
            
this.Name = name;
            
this.Version = version;
            
this.Size = size;
        }
        [XmlAttribute("name")]
        
public string Name{get;set;}
        [XmlAttribute("version")]
        
public string Version{get;set;}
        [XmlAttribute("size")]
        
public long Size { getset; }
    }

 

DownloadInfo.cs
public class DownloadFileInfo
    {
        
public DownloadFileInfo(string name, string url, string ver1, string ver2, long size)
        {
            
this.Name = name;
            
this.DownloadUrl = url;
            
this.OldVersion = ver1;
            
this.NewVersion = ver2;
            
this.Size = size;
        }
        
/// <summary>
        
/// 文件名
        
/// </summary>
        public string Name
        {
            
get;set;
        }
        
/// <summary>
        
/// 从哪里下载
        
/// </summary>
        public string DownloadUrl
        {
            
get;set;
        }
        
/// <summary>
        
/// 版本号
        
/// </summary>
        public string OldVersion
        {
            
get;set;
        }
        
public string NewVersion
        {
            
get;set;
        }
        
/// <summary>
        
/// 大小
        
/// </summary>
        public long Size
        {
            
get;set;
        }
    }

 

 

 

有了这三个结构就可以描述文件信息了,

 程序先检测到更新信息之后添加一个List<DownloadFinleInfo> 列表,然后开始更新,

 实际下载更新的核心代码如下:

 

StartUpdate()
 private static long DownloadedTotal = 0;
        
private static int nindex = 0;
        
private ManualResetEvent alldone = new ManualResetEvent(false);
        
/// <summary>
        
/// 更新线程
        
/// </summary>
        
/// <param name="obj"></param>
        private void StartUpdate(object obj)
        {
            List
<string> okfiles=new List<string>();
            
int ret=0,nok=0,nerr=0;
            StatusdHandler status 
= new StatusdHandler(RefreshResult);
            
foreach(DownloadFileInfo file in DownloadList)
            {
                DownloadedTotal 
= 0;
                alldone.Reset();
                
using(WebClient client=new WebClient())
                {
                    client.DownloadProgressChanged 
+= new DownloadProgressChangedEventHandler(DownloadChanged);
                    client.DownloadFileCompleted 
+= new System.ComponentModel.AsyncCompletedEventHandler(DownloadCompleted);
                    
/*相对路径标示文件保存位置*/
                    
string tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "update", file.Name);
                    
if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
                        Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
                    
string truePath=Path.GetDirectoryName(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,file.Name));
                    
if (!Directory.Exists(truePath))
                        Directory.CreateDirectory(truePath);
                    client.DownloadFileAsync(
new Uri(file.DownloadUrl), tempPath);
                }
                alldone.WaitOne();
                ret
++;
                
if (DownloadedTotal >= file.Size) //根据下载列表文件的大小校准实际下载文件大小
                {
                    nok
++;
                    DownloadView.Invoke(refresh, 
new object[] { nindex, 4"更新成功" });
                    
//更新成功的添加到本地更新列表中
                    config.UpdateFileList.Add(new LocalFile(file.Name, file.NewVersion, file.Size));
                    okfiles.Add(file.Name);
                }
                
else
                {
                    nerr
++;
                    DownloadView.Invoke(refresh, 
new object[] { nindex, 4"更新失败" });
                }
                DownloadResult.Invoke(status, 
new object[] { ret });   
                nindex
++;
            }
            
//更新本地列表文件,如果一个都没更新成功,则不更新本地列表
            if(nok>0)
             config.SaveConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FILE));
            
if(DialogResult.OK==MessageBox.Show(string.Format("更新完毕!成功:{0}个,失败:{1}个",nok,nerr),"更新程序",MessageBoxButtons.OK,MessageBoxIcon.Information))
            {
                
//生成批处理文件用于重启程序
                string bat=Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"update.bat");
                
if (File.Exists(bat))
                    File.Delete(bat);
                
using(StreamWriter sw=new StreamWriter(bat,true))
                {
                    sw.WriteLine(
"@echo off");
                    sw.WriteLine(
":find");
                    FileInfo appexe 
= new FileInfo(Application.ExecutablePath);
                    sw.WriteLine(
string.Format("tasklist|find \"{0}\" ||goto next", appexe.Name));
                    sw.WriteLine(
"goto find");
                    sw.WriteLine(
":next");
                    
foreach (string name in okfiles)
                    {
                         sw.WriteLine(
string.Format("copy update\\{0} {1} /y",name,name));
                    }
                    sw.WriteLine(
"start "+Application.ExecutablePath);
                    sw.WriteLine(
"del %0");
                    sw.WriteLine(
"exit");
                }
                System.Diagnostics.Process.Start(bat);
                Environment.Exit(
0);
            }
        }
        
/// <summary>
        
/// 更新完成
        
/// </summary>
        
/// <param name="obj"></param>
        
/// <param name="e"></param>
        private void DownloadCompleted(object obj,System.ComponentModel.AsyncCompletedEventArgs e)
        {
            alldone.Set();
        }
        
/// <summary>
        
/// 下载过程
        
/// </summary>
        
/// <param name="obj"></param>
        
/// <param name="e"></param>
        private void DownloadChanged(object obj, DownloadProgressChangedEventArgs e)
        {
            DownloadedTotal 
+= e.BytesReceived;
            DownloadView.Invoke(refresh, 
new object[] { nindex, 4string.Format("已更新:{0}/%",e.ProgressPercentage)}); 
        }

 

 

 

 如上基本就是一个完整的自动更新示例了,比较值得注意的一点是在更新的时候先将需要更新的文件下载至软件目录下的update文件夹

 更新完成之后创建一个bat文件结束自身进程,复制update目录文件到目标路径,然后重启进程

 当然处理方法也很多,大家也都有自己的处理方法。

 

 如果此文对大家有所帮助,那也是功德一件,最后附上完整upodate.dll模块项目,如各位有再此基础上进行完善和增强开发的不妨也发我一份。

 

项目下载:/Files/cxwx/Update.rar

 

 

posted @ 2010-06-29 13:21  lianghugg  阅读(975)  评论(1编辑  收藏  举报