【转载】C# WinForm通用自动更新器
C# WinForm通用自动更新器
对于C/S架构来说,软件更新是一个很常用的功能,下面介绍一种非常实用的软件自动升级方案。
二、示意图
3.1、项目创建
四、LinkTo.Toolkit
5.1、实体类
作用:应用程序全局静态常量。全局参数都在此设置,方便统一管理。注:客户端是否检测更新,也是在此设置默认值。
////// 应用程序全局静态常量 /// public static class GlobalParam { #region 自动更新参数 /// /// 是否检查自动更新:默认是true /// public static string CheckAutoUpdate = "true"; /// /// 本地自动更新配置XML文件名 /// public const string AutoUpdateConfig_XmlFileName = "AutoUpdateConfig.xml"; /// /// 本地自动更新下载临时存放目录 /// public const string TempDir = "Temp"; /// /// 远端自动更新信息XML文件名 /// public const string AutoUpdateInfo_XmlFileName = "AutoUpdateInfo.xml"; /// /// 远端自动更新文件存放目录 /// public const string RemoteDir = "AutoUpdateFiles"; /// /// 主线程名 /// public const string MainProcess = "AutoUpdaterTest"; #endregion }
作用:应用程序上下文。
////// 应用程序上下文 /// public class AppContext { /// /// 客户端配置文件 /// public static AutoUpdateConfig AutoUpdateConfigData { get; set; } }
作用:应用程序配置。
public class AppConfig { private static readonly object _lock = new object(); private static AppConfig _instance = null; #region 自动更新配置 ////// 自动更新配置数据 /// public AutoUpdateConfig AutoUpdateConfigData { get; set; } private AppConfig() { AutoUpdateConfigData = new AutoUpdateConfig(); } public static AppConfig Instance { get { if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new AppConfig(); } } } return _instance; } } /// /// 本地自动更新下载临时文件夹路径 /// public string TempPath { get { return string.Format("{0}\\{1}", Application.StartupPath, GlobalParam.TempDir); } } /// /// 初始化系统配置信息 /// public void InitialSystemConfig() { AutoUpdateConfigData.AutoUpdateMode = AppContext.AutoUpdateConfigData.AutoUpdateMode; AutoUpdateConfigData.AutoUpdateHttpUrl = AppContext.AutoUpdateConfigData.AutoUpdateHttpUrl; } #endregion }
5.3、工具类
作用:配置自动更新模式及相关。
注1:复制到输出目录选择始终复制。
注2:主程序运行时,先读取此配置更新文件,然后给AppContext上下文赋值,接着给AppConfig配置赋值。
<?xml version="1.0" encoding="utf-8" ?> <AutoUpdateConfig> <AutoUpdateMode>HTTPAutoUpdateMode> <AutoUpdateHttpUrl>http://127.0.0.1:6600/AutoUpdateDirAutoUpdateHttpUrl> AutoUpdateConfig>
5.5、主程序
作用:检测应用程序是否需要自动更新,如里需要则检测远程服务端的版本号。假如远程服务端有新版本,则调用自动更新器AutoUpdater并向其传递4个参数。
internal static class Program { ////// 应用程序的主入口点 /// [STAThread] private static void Main(string[] args) { //尝试设置访问权限 FileUtility.AddSecurityControll2Folder("Users", Application.StartupPath); //未捕获的异常处理 Application.ThreadException += Application_ThreadException; Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; //是否检查自动更新赋值 if (args.Length > 0) { GlobalParam.CheckAutoUpdate = args[0]; } //加载自动更新配置文件,给上下文AppServiceConfig对象赋值。 var config = AutoUpdateHelper.Load(); AppContext.AutoUpdateConfigData = config; //窗体互斥体 var instance = new Mutex(true, GlobalParam.MainProcess, out bool isNewInstance); if (isNewInstance == true) { if (GlobalParam.CheckAutoUpdate.ToBoolean()) { if (CheckUpdater()) ProcessUtility.KillProcess(GlobalParam.MainProcess); } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(MainForm.Instance); instance.ReleaseMutex(); } else { MessageBox.Show("已经启动了一个程序,请先退出。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error); Application.Exit(); } } /// /// 自动更新检测 /// /// private static bool CheckUpdater() { if (GlobalParam.CheckAutoUpdate.ToBoolean() == false) return false; #region 检查版本更新 Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); bool blFinish = false; AppConfig.Instance.InitialSystemConfig(); var helper = new AutoUpdateHelper(AppConfig.Instance.AutoUpdateConfigData.AutoUpdateMode); string fileVersion = FileVersionInfo.GetVersionInfo(Application.ExecutablePath).FileVersion; long localVersion = 0; long remoteVersion = 0; try { localVersion = fileVersion.Replace(".", "").ToLong(); remoteVersion = helper.GetRemoteAutoUpdateInfoVersion().Replace(".", "").ToLong(); if ((localVersion > 0) && (localVersion < remoteVersion)) { blFinish = true; string autoUpdateConfigXmlPath = helper.WriteLocalAutoUpdateInfoXml(); string autoUpdateInfoXmlPath = Path.Combine(AppConfig.Instance.TempPath, GlobalParam.AutoUpdateInfo_XmlFileName); string argument1 = autoUpdateConfigXmlPath; string argument2 = autoUpdateInfoXmlPath; string argument3 = GlobalParam.MainProcess; string argument4 = GlobalParam.RemoteDir; string arguments = argument1 + " " + argument2 + " " + argument3 + " " + argument4; Process.Start("AutoUpdater.exe", arguments); Application.Exit(); } } catch (TimeoutException) { blFinish = false; } catch (WebException) { blFinish = false; } catch (Exception) { blFinish = false; } return blFinish; #endregion } /// /// UI线程未捕获异常处理 /// /// /// private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) { string strError = "", strLog = "", strDateInfo = DateTime.Now.ToString() + " 出现应用程序未处理的异常:\r\n"; var error = e.Exception; if (error != null) { strError = strDateInfo + $"异常类型:{error.GetType().Name}\r\n异常消息:{error.Message}"; strLog = strDateInfo + $"异常类型:{error.GetType().Name}\r\n异常消息:{error.Message}\r\n堆栈信息:{error.StackTrace}\r\n来源信息:{error.Source}\r\n"; } else { strError = $"Application ThreadException:{e}"; } WriteLog(strLog); MessageBox.Show(strError, "系统错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } /// /// 非UI线程未捕获异常处理 /// /// /// private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { string strError = "", strLog = "", strDateInfo = DateTime.Now.ToString() + " 出现应用程序未处理的异常:\r\n"; if (e.ExceptionObject is Exception error) { strError = strDateInfo + $"异常消息:{error.Message}"; strLog = strDateInfo + $"异常消息:{error.Message}\r\n堆栈信息:{error.StackTrace}"; } else { strError = $"Application UnhandledError:{e}"; } WriteLog(strLog); MessageBox.Show(strError, "系统错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } /// /// 写入日志 /// /// private static void WriteLog(string strLog) { string dirPath = @"Log\MainProcess", fileName = DateTime.Now.ToString("yyyy-MM-dd") + ".txt"; string strLine = "----------------------------------------------------------------------------------------------------"; FileUtility.WriteFile(Path.Combine(dirPath, fileName), strLog); FileUtility.WriteFile(Path.Combine(dirPath,fileName), strLine); } }
六、AutoUpdater
作用:配置自动更新模式及相关。
////// 自动更新配置信息 /// public class AutoUpdateConfig { /// /// 自动升级模式:当前仅支持HTTP /// public string AutoUpdateMode { get; set; } /// /// HTTP自动升级模式时的URL地址 /// public string AutoUpdateHttpUrl { get; set; } }
作用:自动更新内容信息。
////// 自动更新内容信息 /// [Serializable] public class AutoUpdateInfo { /// /// 新版本号 /// public string NewVersion { get; set; } /// /// 更新日期 /// public string UpdateTime { get; set; } /// /// 更新内容说明 /// public string UpdateContent { get; set; } /// /// 更新文件列表 /// public List<string> FileList { get; set; } }
6.2、通用类
新建一个Windows 窗体HttpStartUp。
public partial class HttpStartUp : Form { private bool _blSuccess = false; private string _autoUpdateHttpUrl = null; private AutoUpdateInfo _autoUpdateInfo = null; public HttpStartUp(string autoUpdateHttpUrl, AutoUpdateInfo autoUpdateInfo) { InitializeComponent(); _autoUpdateHttpUrl = autoUpdateHttpUrl; _autoUpdateInfo = autoUpdateInfo; _blSuccess = false; } ////// 窗体加载事件 /// /// /// private void Main_Load(object sender, EventArgs e) { Text = GlobalParam.MainProcess + "-更新程序"; lblUpdateTime.Text = _autoUpdateInfo.UpdateTime; lblNewVersion.Text = _autoUpdateInfo.NewVersion; txtUpdate.Text = _autoUpdateInfo.UpdateContent; } /// /// 立即更新 /// /// /// private void btnRun_Click(object sender, EventArgs e) { ProcessUtility.KillProcess(GlobalParam.MainProcess); btnRun.Enabled = false; btnLeave.Enabled = false; Thread thread = new Thread(() => { try { var downFileList = _autoUpdateInfo.FileList.OrderByDescending(s => s.IndexOf("\\")); foreach (var fileName in downFileList) { string fileUrl = string.Empty, fileVaildPath = string.Empty; if (fileName.StartsWith("\\")) { fileVaildPath = fileName.Substring(fileName.IndexOf("\\")); } else { fileVaildPath = fileName; } fileUrl = _autoUpdateHttpUrl.TrimEnd(new char[] { '/' }) + @"/" + GlobalParam.RemoteDir + @"/" + fileVaildPath.Replace("\\", "/"); //替换文件目录中的路径为网络路径 DownloadFileDetail(fileUrl, fileName); } _blSuccess = true; } catch (Exception ex) { BeginInvoke(new MethodInvoker(() => { throw ex; })); } finally { BeginInvoke(new MethodInvoker(delegate () { btnRun.Enabled = true; btnLeave.Enabled = true; })); } if (_blSuccess) { Process.Start(GlobalParam.MainProcess + ".exe"); BeginInvoke(new MethodInvoker(delegate () { Close(); Application.Exit(); })); } }) { IsBackground = true }; thread.Start(); } private void DownloadFileDetail(string httpUrl, string filename) { string fileName = Application.StartupPath + "\\" + filename; string dirPath = GetDirPath(fileName); if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } HttpWebRequest request = (HttpWebRequest)WebRequest.Create(httpUrl); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream httpStream = response.GetResponseStream(); long totalBytes = response.ContentLength; if (progressBar != null) { BeginInvoke(new MethodInvoker(delegate () { lblDownInfo.Text = "开始下载..."; progressBar.Maximum = (int)totalBytes; progressBar.Minimum = 0; })); } FileStream outputStream = new FileStream(fileName, FileMode.Create); int bufferSize = 2048; int readCount; byte[] buffer = new byte[bufferSize]; readCount = httpStream.Read(buffer, 0, bufferSize); int allByte = (int)response.ContentLength; int startByte = 0; BeginInvoke(new MethodInvoker(delegate () { progressBar.Maximum = allByte; progressBar.Minimum = 0; })); while (readCount > 0) { outputStream.Write(buffer, 0, readCount); readCount = httpStream.Read(buffer, 0, bufferSize); startByte += readCount; BeginInvoke(new MethodInvoker(delegate () { lblDownInfo.Text = "已下载:" + startByte / 1024 + "KB/" + "总长度:"+ allByte / 1024 + "KB" + " " + " 文件名:" + filename; progressBar.Value = startByte; })); Application.DoEvents(); Thread.Sleep(5); } BeginInvoke(new MethodInvoker(delegate () { lblDownInfo.Text = "下载完成。"; })); httpStream.Close(); outputStream.Close(); response.Close(); } public static string GetDirPath(string filePath) { if (filePath.LastIndexOf("\\") > 0) { return filePath.Substring(0, filePath.LastIndexOf("\\")); } return filePath; } /// /// 暂不更新 /// /// /// private void btnLeave_Click(object sender, EventArgs e) { if (MessageBox.Show("确定要放弃此次更新吗?", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { Process.Start(GlobalParam.MainProcess + ".exe", "false"); Close(); Application.Exit(); } } }
6.4、应用程序主入口
7.1、实体类
作用:应用程序全局静态常量。全局参数都在此设置,方便统一管理。
////// 应用程序全局静态常量 /// public static class GlobalParam { /// /// 远端自动更新信息XML文件名 /// public const string AutoUpdateInfo_XmlFileName = "AutoUpdateInfo.xml"; /// /// 远端自动更新目录 /// public const string AutoUpdateDir = "AutoUpdateDir"; /// /// 远端自动更新文件存放目录 /// public const string RemoteDir = "AutoUpdateFiles"; /// /// 主线程名 /// public const string MainProcess = "AutoUpdaterTest"; }
7.3、Window 窗体
internal static class Program { ////// 应用程序的主入口点。 /// [STAThread] private static void Main() { Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Main()); } /// /// UI线程未捕获异常处理 /// /// /// public static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { string strError = "", strLog = "", strDateInfo = DateTime.Now.ToString() + " 出现应用程序未处理的异常:\r\n"; var error = e.Exception; if (error != null) { strError = strDateInfo + $"异常类型:{error.GetType().Name}\r\n异常消息:{error.Message}"; strLog = strDateInfo + $"异常类型:{error.GetType().Name}\r\n异常消息:{error.Message}\r\n堆栈信息:{error.StackTrace}\r\n来源信息:{error.Source}\r\n"; } else { strError = $"Application ThreadException:{e}"; } WriteLog(strLog); MessageBox.Show(strError, "系统错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } /// /// 非UI线程未捕获异常处理 /// /// /// public static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { string strError = "", strLog = "", strDateInfo = DateTime.Now.ToString() + " 出现应用程序未处理的异常:\r\n"; if (e.ExceptionObject is Exception error) { strError = strDateInfo + $"异常消息:{error.Message}"; strLog = strDateInfo + $"异常消息:{error.Message}\r\n堆栈信息:{error.StackTrace}"; } else { strError = $"Application UnhandledError:{e}"; } WriteLog(strLog); MessageBox.Show(strError, "系统错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } /// /// 写入日志 /// /// private static void WriteLog(string strLog) { string dirPath = @"Log\XmlBuilder", fileName = DateTime.Now.ToString("yyyy-MM-dd") + ".txt"; string strLine = "----------------------------------------------------------------------------------------------------"; FileUtility.WriteFile(Path.Combine(dirPath, fileName), strLog); FileUtility.WriteFile(Path.Combine(dirPath, fileName), strLine); } }
八、远程服务端配置
注:此处为本机测试。
1)在某个盘符如E盘下新建一个AutoUpdate文件夹,将AutoUpdateXmlBuilder打包文件夹AutoUpdateDir拷贝到AutoUpdate文件夹下。
2)在IIS中新建一个网站,对应的虚拟目录为E:\AutoUpdate,同时将端口设置为6600。
3)运行AutoUpdaterTest,如出现自动更新器即代表成功。
源码下载
分类: 缥缈的尘埃关注 - 8
粉丝 - 126 我在关注他 ? 上一篇: SQL Server读取及导入Excel数据
? 下一篇: SQL Server高级进阶之索引碎片维护 posted @ 2021-09-13 13:16 缥缈的尘埃 阅读(598) 评论(12) 编辑 收藏 举报