读书人

自动升级的原理及C#代码实现解决思路

发布时间: 2012-01-19 20:57:59 作者: rapoo

自动升级的原理及C#代码实现
原文地址,原文的格式更规范写
http://blog.csdn.net/xuexiaodong2009/article/details/6640323
C/S程序自动升级是一个很重要的功能,原理其实很简单,一般包含两个程序一个是主程序,也就是除了升级功能以外的程序,另一个就是升级程序,常见的360,金山安全卫士都是这样。

主要包括以下几点: 1 比较版本 2下载文件 3更新文件 4启动主程序。但其中的需要注意的细节很多。


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

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

缺点:如果更新速度由于更新的文件很大或者网速很慢,用户不得不等待很长时间,直到下载完成或者下载失败。

优点:处理完成后,启动的直接就是更新后的程序。不会出现由于主程序在运行导致替换文件之类的错误。

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

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

优点:避免了由于下载导致用户长时间的等待。

1 比较版本

比较依据:

可以通过文件的最后修改时间,或者使用文件版本作为比较依据,使用文件最后修改时间显然不是标准的做法,但也没有错误,但需要注意日期的格式一定要统一,避免日 期格式不一致导致错误。可以使用Fileinfo类获取最后修改时间。

FIleInfo类官网参考
http://forum.csdn.net/PointForum/Forum/PostTopic.aspx?forumID=e2798a59-79d5-4833-9c57-87d46a8b907a

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

C# code
    Assembly thisAssem = Assembly.GetExecutingAssembly();           AssemblyName thisAssemName = thisAssem.GetName();           Version ver = thisAssemName.Version;  

Version类官网参考
http://msdn.microsoft.com/zh-cn/library/system.version.aspx

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

当然如果考虑客户端有不同的版本,都需要升级到最新的版本,显然不同的版本对应的升级文件不同,会更复杂,比较的信息也更多。

获取服务端版本信息:

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

2下载文件

存储位置:

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

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

下载方式:

直接向通过虚拟路径发出请求,下载文件,由于虚拟路径有下载权限,如果更新需要判断是否有权限,例如要交费后才能下载则不好处理。

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

下载文件代码

C# code
 Uri uri = new Uri(downFileUrl + localFileName);                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);                    request.Credentials = CredentialCache.DefaultCredentials;                    request.MaximumAutomaticRedirections = 4;                    localFileName = Path.GetFileName(localFileName);                    using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())                    {                        Stream receiveStream = response.GetResponseStream();                        string newPath = Path.Combine(tempFold, localFileName);                        using (FileStream fs = new FileStream(newPath, FileMode.Create))                        {                            Byte[] buffer = new Byte[4096];                            int bytesRead = receiveStream.Read(buffer, 0, buffer.Length);                            while (bytesRead > 0){                                fs.Write(buffer, 0, bytesRead);                                bytesRead = receiveStream.Read(buffer, 0, buffer.Length);                            }                        }                        receiveStream.Close();                    }  


3更新文件

更新类型:

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



新增加的,例如新增加的功能做成了新的类库。

需要删除的,例如有些功能由于重构或者使用了了新方法不需要的。

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

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

缺点:升级后,没办法取消升级,像windows的补丁程序可以安装,可以卸载的原理,目前还没有研究明白,希望知道的牛人指导。

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

当然如果考虑客户端有不同的版本,都需要升级到最新的版本,显然不同的版本对应的升级文件不同,会更复杂,但基本原理却不变。

4启动主程序

主程序路径的获取:

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

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

运行程序代码

C# code
 private static void RunFile(string dir, string localFileName){                string info = "运行程序" + localFileName;                try{                    if (File.Exists(Path.Combine(dir, localFileName))){                        Process myProcess = new Process();                        ProcessStartInfo psi = new ProcessStartInfo();                        psi.FileName = localFileName;                        psi.WorkingDirectory = dir;                        psi.UseShellExecute = false;                        psi.RedirectStandardError = true;                        psi.CreateNoWindow = true;                        psi.RedirectStandardOutput = true;                        psi.WindowStyle = ProcessWindowStyle.Hidden;                        myProcess.StartInfo = psi;                        myProcess.Start();                              string error = myProcess.StandardError.ReadToEnd();                        string output = myProcess.StandardOutput.ReadToEnd();                        myProcess.WaitForExit();                        myProcess.Close();                        if (error != string.Empty){                         Log.Write("StandardError:" + error);                        }                        if (output != string.Empty){                            Log.Write("StandardOutput:" + output);                        }                        Log.LogProcessEnd(info);                    }                }                catch (Exception ex){                   Log.Write(info + "出错");                   Log.LogException(ex);                    throw ex;                }            }        }  

源代码下载
http://download.csdn.net/source/3477103



[解决办法]
这个读取EXE/DLL(VS2010编写的程序)的相关信息"版本信息"吗
[解决办法]
按照不同版本做备份就可以实现回滚,或取消了。
[解决办法]
探讨

引用:

这个读取EXE/DLL(VS2010编写的程序)的相关信息"版本信息"吗
代码使用的是文件的最后修改时间,没有使用版本号

[解决办法]
更新自己怎么办,也就说【更新程序】本身需要更新。
[解决办法]
下载的位置好像没有考虑用户代理上网的情况。

[解决办法]
探讨

更新自己怎么办,也就说【更新程序】本身需要更新。

[解决办法]
这个太好了
学习
学习
支持
[解决办法]
探讨
不过个人认为文件大小缺陷很明显,新版本文件就一定比旧文件大吗?不一定吧。重构是可能变小的。

[解决办法]
探讨



按照不同版本做备份就可以实现回滚,或取消了。


[解决办法]
不错,自动更新可以分为2大类:
1。完全更新,比较适合体积比较小程序,直接做成安装文件,发现更新后,下载完整安装包,安装是关闭程序并更新掉,再启动;次类型简单,维护简单,网络上很多共享免费小软件都是次类更新。

2。部分更新,比较适合大程序,大程序可能每次更新都下载完整文件,所以需要做版本比较后,确认需要更新的文件,然后按需下载,然后启动更新,替换本地文件后,启动程序;次方法可以解决大体积程序下载过慢的问题,减少服务器的流量负担,一般的商业系统都是此类方法。

读书人网 >C#

热点推荐