自动升级的原理及实现

  C/S程序自动升级是一个很重要的功能,原理其实很简单,通常包含两个程序一个是主程序,也就是除了升级功能之外的程序,另外一个就是升级程序,常见的360,金山安全卫士都是这样。html

 主要包括如下几点:   1 比较版本  2下载文件  3更新文件 4启动主程序。但其中的须要注意的细节不少。 web

    通常服务端会有一个配置文件包含最新更新的文件信息的配置文件,固然这些更新信息也能够存到数据库,或者其余地方。客户端(也就是须要更新的那部分程序)也有一个配置文件包含客户端版本信息,这些信息能够存到专门的一个配置文件中,或者是config文件中,没有必定的规定,能够根据实际设计。数据库

   在客户端程序启动时,先启动更新程序经过比较本地版本和服务端最新的版本信息判断是否有新版本,若是有能够直接下载,下载完成替换成功后并更新客户端版本信息,启动主程序。windows

            缺点:若是更新速度因为更新的文件很大或者网速很慢,用户不得不等待很长时间,直到下载完成或者下载失败。安全

            优势:处理完成后,启动的直接就是更新后的程序。不会出现因为主程序在运行致使替换文件时提示文件在使用,不能替换之类的错误。服务器

   另外一种方法是, 在客户端段程序启动时,启动更新程序,但更新程序不作版本判断,到客户端更新目录下检查有没有下载的新版本,若是有就更新主程序并更新客户端版本信息,而后启动主程序,若是没有就直接启动主程序。由主程序判断是否有新版本,并在后台下载把文件放到客户端更新目录中,下载完成后,提示用户退出主程序,从新启动,在启动时由更新程序并更新客户端和客户端版本信息。    性能

            缺点:因为下载是在主程序的后台运行,可能会影响主程序的处理速度。网站

            优势:避免了因为下载致使用户长时间的等待。ui

1 比较版本this

    比较依据:

    能够经过文件的最后修改时间,或者使用文件版本做为比较依据,使用文件最后修改时间显然不是标准的作法,但也没有错误,但须要注意日期的格式必定要统一,避免日  期格式不一致致使错误。可使用Fileinfo类获取最后修改时间,注意时间应该取服务器时间,编译程序集的机器时间应该相同,不然可能致使混乱。

FIleInfo类官网参考

   使用文件版本做为标准,则每次修改时必须修改版本号,C#程序就是要修AssemblyInfo.cs文件中的内容了,多了一步,规范多了。Version类处理版本信息并比较。 

[csharp]  view plain copy
 
  1. Assembly thisAssem = Assembly.GetExecutingAssembly();  
  2.      AssemblyName thisAssemName = thisAssem.GetName();  
  3.      Version ver = thisAssemName.Version;  

Version类官网参考

  固然也有其余的方式,例如MD5校验值比较,文件大小比较,之类的方法。不过我的认为文件大小缺陷很明显,新版本文件就必定比旧文件大吗?不必定吧。重构是可能变小的。

固然若是考虑客户端有不一样的版本,都须要升级到最新的版本,显然不一样的版本对应的升级文件不一样,会更复杂,比较的信息也更多。

   获取服务端版本信息:

    若是服务端的版本信息存在数据库,直接读取数据库,就能够获取。若是存在配置文件,则能够经过webservice方法获取,或者请求一个网页 经过Response.Write();的方式获取信息,固然这两种方式都要创建虚拟目录或者网站。

2下载文件

  存储位置:

     若是新版本的文件存在数据库,就直接读取数据库,不过这种方式我的不建议使用,例如更新文件很大时性能不是很好。

    存在固定虚拟目录的指定路径下,不失为一种好的方式,但客户端要下载,因此要注意必定要分配下载权限。

下载方式:

   直接向经过虚拟路径发出请求,下载文件,因为虚拟路径有下载权限,若是更新须要判断是否有权限,例如要交费后才能下载则很差处理。

  另外一种方式是向一个网页发送请求,传递不一样的查询字符串,网页 经过Response.BinaryWrite();的方式下载文件,则能够判断权限,固然麻烦一些是避免不了的。

下载文件代码

[csharp]  view plain copy
 
  1. Uri uri = new Uri(downFileUrl + localFileName);  
  2.               HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);  
  3.               request.Credentials = CredentialCache.DefaultCredentials;  
  4.               request.MaximumAutomaticRedirections = 4;  
  5.               localFileName = Path.GetFileName(localFileName);  
  6.               using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())  
  7.               {  
  8.                   Stream receiveStream = response.GetResponseStream();  
  9.                   string newPath = Path.Combine(tempFold, localFileName);  
  10.                   using (FileStream fs = new FileStream(newPath, FileMode.Create))  
  11.                   {  
  12.                       Byte[] buffer = new Byte[4096];  
  13.                       int bytesRead = receiveStream.Read(buffer, 0, buffer.Length);  
  14.                       while (bytesRead > 0){  
  15.                           fs.Write(buffer, 0, bytesRead);  
  16.                           bytesRead = receiveStream.Read(buffer, 0, buffer.Length);  
  17.                       }  
  18.                   }  
  19.                   receiveStream.Close();  
  20.               }  

3更新文件

      更新类型:

     直接替换的,例如修改了bug,直接替换的。

     新增长的,例如新增长的功能作成了新的类库。

    须要删除的,例若有些功能因为重构或者使用了了新方法不须要的。

     须要执行的,例如写注册表,注册COM组件的。

     每一种处理方式都不同,须要根据类型分开处理

     缺点:升级后,没办法取消升级,像windows的补丁程序能够安装,能够卸载的原理,目前尚未研究明白,但愿知道的牛人指导。

    固然也能够简单的先卸载,再安装,对于配置文件之类的信息特殊处理一下也能够。

   固然若是考虑客户端有不一样的版本,都须要升级到最新的版本,显然不一样的版本对应的升级文件不一样,会更复杂,但基本原理却不变。

4启动主程序

  主程序路径的获取:

   相对路径   主程序,更新程序,都使用相对路径,缺点是一旦相对路径肯定后,后续的更新就不能更改这种目录关系。

  注册表  路径都存入注册表,须要时经过注册表交互,主程序写注册表,更新程序读取注册表,缺点是读写注册表须要权限,写的路径也要固定,后续的更新不能改变写在注册表中的位置,也就是注册表路径。

运行程序代码

 

[csharp]  view plain copy
 
  1. private static void RunFile(string dir, string localFileName){  
  2.           string info = "运行程序" + localFileName;  
  3.           try{  
  4.               if (File.Exists(Path.Combine(dir, localFileName))){  
  5.                   Process myProcess = new Process();  
  6.                   ProcessStartInfo psi = new ProcessStartInfo();  
  7.                   psi.FileName = localFileName;  
  8.                   psi.WorkingDirectory = dir;  
  9.                   psi.UseShellExecute = false;  
  10.                   psi.RedirectStandardError = true;  
  11.                   psi.CreateNoWindow = true;  
  12.                   psi.RedirectStandardOutput = true;  
  13.                   psi.WindowStyle = ProcessWindowStyle.Hidden;  
  14.                   myProcess.StartInfo = psi;  
  15.                   myProcess.Start();  
  16.   
  17.                   string error = myProcess.StandardError.ReadToEnd();  
  18.                   string output = myProcess.StandardOutput.ReadToEnd();  
  19.                   myProcess.WaitForExit();  
  20.                   myProcess.Close();  
  21.                   if (error != string.Empty){  
  22.                    Log.Write("StandardError:" + error);  
  23.                   }  
  24.                   if (output != string.Empty){  
  25.                       Log.Write("StandardOutput:" + output);  
  26.                   }  
  27.                   Log.LogProcessEnd(info);  
  28.               }  
  29.           }  
  30.           catch (Exception ex){  
  31.              Log.Write(info + "出错");  
  32.              Log.LogException(ex);  
  33.               throw ex;  
  34.           }  
  35.       }  
  36.   }  

源代码下载

参考文章

参考文章2

参考文章3

相关文章
相关标签/搜索