这里说的是C/S客户端更新,其他情况不做讨论

  • 1.更新程序减少依赖

之前做过一次更相信需要更新Newtonsoft.Json.dll 的版本

但是我的更新程序引用了这个文件 也就是说执行更新程序的时候这个文件是占用状态,更新不了!

所以更新程序最好是零引用,只用系统自带的dll类库就可以,尽可能【小】

  • 2.更新的脚本

软件更新有时候需要对系统设置进行一些更改,例如注册表,防火墙之类的,这个时候就需要执行一些脚本来写入注册表或开启防火墙端口

这里可以设定一个执行脚本,需要注意的是和更新程序一样,脚本的引用支持程序要简单,你不能为了执行脚本就要用户装一个脚本解析器。

额外说一句虽然vbs脚本系统默认都是认的,但是我也有遇到过【没有找到文件扩展VBS的脚本引擎】这样的错误,虽然可以修复这个错误但是也需要尽可能避免这个问题,用户不会在乎错误原因是什么,只会认为:“我用了你的系统报了一个错误”

  • 3.容灾

更新不一定能够绝对成功的,就是你的代码再完美,用户操作可能不合法,可能被杀软拦截,可能操作系统本身就有问题,等等意外,这个时候首先尽可能保证更新失败也可以正常使用系统,如果旧版本确实放弃了,无法使用,可以给出友好提示,联系管理员之类的。

  • 4.版本检查

如果不是最新版本不允许使用软件系统,软件有一个BUG不修复的话会导致系统异常,也就是不更新软件没法使用,这个时候需要检查版本,如果版本不对则直接禁止使用。

这个功能比容灾更复杂一些,首先为了保证设定有效控制,需要将限制放在服务端,然后客户端使用的时候将自己的版本号告诉服务端,服务端检查后决定是否放行。

版本号分为主程序版本号以及引用文件版本号,可能主程序版本号没有问题,但是某一个功能版本号没有更新,则这个功能无法使用,其他功能不受影响

  • 5.方案

这里记录一下我正在使用的方案,有不少不足之处,先记下来,后面优化

首先服务器上放一个txt文件,更新时读取这个txt文件

txt里面包含软件最新版本,软件每个需要用到的文件的版本,用于比较最新版本然后下载有更新的文件

为了生成这样的txt文件我还写了一个生成的小工具

然后时vbs脚本放在服务器端,更新时下载到客户端执行(如上文描述,有部分客户端操作系统提示错误【没有找到文件扩展VBS的脚本引擎】,后面准备用bat或exe)

vbs里面是一个延时执行的代码,比如【更新程序】时a.exe 如果要更新【更新程序】本身则将新版本的【更新程序】重命名为a2.exe放在服务端,然后下载到客户端,因为延时程序设定在a.exe退出后删除a.exe然后将a2.exe重命名为a.exe,以实现更新自身

同时vbs里面也有删除函数,用来清除多余的文件

容灾方案正在设计中……

设想:将旧版本重命名,追加【.old】,然后将新版本文件下载追加【.new】,所有下载完成之后再统一删除重命名,如果出错则把【.old】恢复

还没有实践,因为加了这个功能本质上违反了【简单】的原则,功能越多越容易发生问题,先将方案实现看看效果

-----------------------正在使用的方案的代码

/******************************************************************************** Copyright © 2010-2020  陈恩点版权所有* Author: 陈恩点* First Create: 2012/8/21 11:49:53* Contact: 18115503914* Description: MyRapid快速开发框架
*********************************************************************************/
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
namespace MyRapid.Launcher
{public partial class MyWait : Form{Process process;string ProcessName = "";string ScriptName = "";string ImagePath = "";string FormName = "主窗体";string urlPath = "";int WaitSecond = 30;Stopwatch sw = new Stopwatch();public MyWait(){sw.Restart();InitializeComponent();//用参数把4个变量传入//分别为//1 ProcessName 程序路径//2 ImagePath 动画路径//3 ScriptName 预更新脚本//4 FormName 启动程序的标识,用于判断程序是否加在完毕ProcessName = GetVal("exePath");ScriptName = GetVal("vbsPath");ImagePath = GetVal("imgPath");FormName = GetVal("exeTitle");MoveHandle(label1);MoveHandle(this);MoveHandle(progressBar1);if (File.Exists("tips.txt")){tips = File.ReadAllLines("tips.txt");}}private void UpdateFile(){try{DateTime ver = DateTime.Parse(GetVal("version"));WebClient wc = new WebClient();wc.Encoding = Encoding.UTF8;label1.Text = "正在检查更新...";string updateFile = "update.txt";urlPath = GetVal("urlPath");wc.DownloadFile(urlPath, updateFile);if (!File.Exists(updateFile)) return;string[] verString = File.ReadAllLines(updateFile);if (verString.Length == 0) return;if (DateTime.Parse(verString[0]) <= ver) return;string fName = Path.GetFileNameWithoutExtension(ProcessName);fName = Path.GetFileName(ProcessName);var prcList = Process.GetProcesses().Where(pr => pr.ProcessName == Path.GetFileNameWithoutExtension(ProcessName) || pr.ProcessName == Path.GetFileName(ProcessName));if (prcList.Count() > 0){label1.Text = "主程序运行中,无法更新...";return;}progressBar1.Style = ProgressBarStyle.Continuous;progressBar1.Maximum = verString.Length;progressBar1.Value = 0;foreach (string sf in verString){//Tipsif (tips != null && tips.Length > 0 && sw.Elapsed.TotalSeconds % 5 == 1){int i = DateTime.Now.Millisecond % (tips.Length - 1);label3.Text = string.Format(tip, tips[i]);this.Refresh();}if (progressBar1.Value < verString.Length)progressBar1.Value += 1;if (!sf.Contains("|")) continue;string[] ups = sf.Split('|');if (DateTime.Parse(ups[0]) <= ver) continue;//如果目录不存在这创建string fDir = Path.GetDirectoryName(ups[1]);if (!string.IsNullOrEmpty(fDir) && !Directory.Exists(fDir)){Directory.CreateDirectory(fDir);}//下载文件label1.Text = ups[1];this.Refresh();wc.DownloadFile(ups[2], ups[1]);}SetVal("version", DateTime.Now.ToString());label1.Text = "更新结束:正在启动主程序...";progressBar1.Style = ProgressBarStyle.Marquee;}catch (Exception ex){label1.Text += "更新失败,请重试或联系管理员协助处理:" + ex.Message;}}private void StartMain(){//执行预更新脚本if (File.Exists(ScriptName)){if (ScriptName.EndsWith(".vbs")){ProcessStartInfo startInfo = new ProcessStartInfo();startInfo.FileName = "wscript.exe";startInfo.Arguments = ScriptName;Process.Start(startInfo);}else{Process script = Process.Start(ScriptName);}}if (File.Exists(ProcessName)){//启动程序ProcessStartInfo processStartInfo = new ProcessStartInfo();processStartInfo.FileName = ProcessName;processStartInfo.WorkingDirectory = Path.GetDirectoryName(ProcessName);process = Process.Start(processStartInfo);}else{Environment.Exit(0);}if (File.Exists(ImagePath)){//加载动画Image image = Image.FromFile(ImagePath);this.BackgroundImage = image;}}private void timer1_Tick(object sender, EventArgs e){//这里是用于判断结束 分两种  超时  或  已完成if (sw.Elapsed.TotalSeconds > WaitSecond)Environment.Exit(0);if (process == null)Environment.Exit(0);if (process.HasExited)Environment.Exit(0);process.Refresh();if (process.MainWindowTitle.Equals(FormName))Environment.Exit(0);//Console.WriteLine(process.MainWindowTitle);//Console.WriteLine(sw.Elapsed.TotalMilliseconds);
        }private void MyWait_Shown(object sender, EventArgs e){this.Refresh();UpdateFile();if (File.Exists("update.txt"))File.Delete("update.txt");sw.Restart();timer1.Enabled = true;StartMain();}#region Tipsstring[] tips;string tip = "小贴士:{0}";#endregion#region Functionpublic string GetVal(string key){try{return ConfigurationManager.AppSettings.Get(key);}catch{throw;}}public void SetVal(string key, string value){try{Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);configuration.AppSettings.Settings[key].Value = value;configuration.Save(ConfigurationSaveMode.Modified);}catch{throw;}}/// <summary>/// 为控件添加移动功能/// </summary>/// <param name="ctrl">鼠标按下的控件</param>/// <param name="who">移动的控件(若不存在则为控件自己) /// 1:控件所在窗体; /// 2:控件的父级; /// 3:控件自己. </param>public void MoveHandle(Control ctrl, int who = 1){try{Control mCtrl;//= ctrl.FindForm();switch (who){case 1:mCtrl = ctrl.FindForm();break;case 2:mCtrl = ctrl.Parent;break;case 3:mCtrl = ctrl;break;default:mCtrl = ctrl.FindForm();break;}if (mCtrl == null){mCtrl = ctrl;}Point sourcePoint = new Point(0, 0);bool isMove = false;ctrl.MouseDown += delegate (object sender, MouseEventArgs e){sourcePoint = e.Location;isMove = true;};ctrl.MouseMove += delegate (object sender, MouseEventArgs e){if (isMove)mCtrl.Location = new Point(mCtrl.Location.X + e.X - sourcePoint.X, mCtrl.Location.Y + e.Y - sourcePoint.Y);};ctrl.MouseUp += delegate (object sender, MouseEventArgs e){isMove = false;};}catch (Exception ex){throw ex;}}#endregion}}

----------配置文件

<?xml version="1.0" encoding="utf-8"?>
<configuration><appSettings><add key="exePath" value="MyRapid.Client.exe"/><add key="vbsPath" value="update.vbs"/><add key="imgPath" value=""/><add key="exeTitle" value="用户登录"/><add key="urlPath" value="http://127.0.0.1:4824/update.txt"/><add key="version" value="2019/9/17 9:12:23" /></appSettings><startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup>
</configuration>

----------服务端update.txt的格式

2019/8/23 16:48:18
2019/8/23 16:48:17|MyRapid.Client.exe|http://myerp.oicp.io:4824/cBest/MyRapid.Client.exe
2019/8/23 13:17:09|MyRapid.Launcher2.exe|http://myerp.oicp.io:4824/cBest/MyRapid.Launcher2.exe
2019/8/19 11:37:20|tips.txt|http://myerp.oicp.io:4824/cBest/tips.txt
2019/8/19 11:28:07|update.rar|http://myerp.oicp.io:4824/cBest/update.rar
2049/8/19 11:27:43|update.vbs|http://myerp.oicp.io:4824/cBest/update.vbs

转载于:https://www.cnblogs.com/myrapid/p/11410722.html

如何做好软件自动更新相关推荐

  1. C#软件自动更新程序

    2019独角兽企业重金招聘Python工程师标准>>> 基于C#实现的软件自动更新程序,之前在网上搜集了两款软件自动更新程序,在实际应用中,对部分BUG进行修复,添加+完善一些功能. ...

  2. Android - 软件自动更新的实现

    Android - 软件自动更新的实现 2012年11月18日 天气慢慢变凉了,给位亲,注意保暖啊. 接触到一个很实用的技术,那就是软件自动更新.一般开发者是通过自行在应用平台添加更新版本的apk.这 ...

  3. 软件自动更新解决方案及QT实现

    from:https://blog.csdn.net/hulinhulin/article/details/46839107 软件自动更新解决放案及QT实现...1 1 文件的版本控制-XML.2 2 ...

  4. 《『若水新闻』客户端开发教程》——17.软件自动更新

    本节课的主要内容: 1.增加软件自动更新功能 课程下载: http://115.com/file/e77ow25d

  5. 软件自动更新功能的实现

    今天一朋友在群里面问,软件自动更新功能怎么做,大家都不知道怎么搞,我下午刚好没事情,就研究了下. 附上我的源代码 考虑下基本的思路 1 .客户端(主程序)调用升级程序,升级程序连接到最新的服务器上. ...

  6. 如何屏蔽 iOS 16 软件自动更新,去除更新通知和标记

    如何禁用 iPhone.iPad 软件自动更新.适用于 iOS.iPadOS 和 watchOS,即 iPhone.iPad 和 Apple Watch 通用. 请访问原文链接:https://sys ...

  7. MAC 关闭office软件自动更新提示 (Microsoft AutoUpdate)

    MAC 关闭office软件自动更新提示 (Microsoft AutoUpdate) cd /Library/Application\ Support/Microsoft/MAU2.0 sudo c ...

  8. 软件自动更新解决方案及QT实现(源码已上传)

    软件自动更新解决放案及QT实现...1 1 文件的版本控制-XML.2 2 更新程序的实现...2 2.1 界面设置...2 2.2 程序功能...3 2.2.1 下载网络数据...3 2.2.2 X ...

  9. 【Electron】酷家乐客户端开发实践分享 — 软件自动更新

    作者:钟离,酷家乐PC客户端负责人 原文地址:webfe.kujiale.com/electron-au- 酷家乐客户端:下载地址 www.kujiale.com/activity/13- 文章背景: ...

最新文章

  1. 网站基于文本搜索的实现
  2. 【Linux】开源分布式存储系统:GlusterFS
  3. Python读写json文件中文编码问题
  4. 【撸码师的备忘录】java对redis的基本操作
  5. DataTables warning: Requested unknown parameter '0' from the data source for row '0'
  6. leetcode 872. 叶子相似的树(dfs)
  7. Struts2中jsp前台传值到action后台的三种方式以及valueStack的使用
  8. ORA-39070:无法打开日志文件
  9. 苹果企业账号炒作到多少钱_从炒作到行动:边缘计算的后续步骤
  10. 使用FlashBoot3.2c 将 U 盘制作成 DOS 启动盘
  11. 白话区块链 之4: 区块链分类与架构
  12. F28335学习(二)EPWM
  13. Element DateTimePicker 日期时间选择器 今天日期设置,并获取value值
  14. Python生成requirement.txt文件
  15. 反病毒垃圾邮件,U-Mail邮件系统从容应对
  16. origin ‘http://localhost:8080‘ has been blocked by CORS policy: Request header field platform is not
  17. 【学术】各类基金资助项目英文翻译(中英文对照)
  18. 最大熵模型怎么理解?熵是什么??
  19. 自媒体运营的八条建议
  20. oracle11g忘记system密码,重置密码

热门文章

  1. Oracle -- Oracle中几个数字函数、转换函数、字符串函数
  2. Jscp (Javascript client page) javascript 库
  3. 如何使用图片制作翻页电子书?
  4. maven-assembly-plugin maven自定义打包
  5. 为什么商务人都申请163邮箱,163邮箱怎么注册登陆呢?
  6. 鲜为人知的Linux命令
  7. 【2.学习__签名证书和加密证书】
  8. 2022年五大开源数据可视化BI方案对比
  9. 2.3 发光二极管(LED灯)
  10. 王芷蕾、天韵诗班-给我换颗心(心灵不打烊)