Windows 程序自动更新方案: Squirrel.Windows (2)

Squirrel 进程通过启动子进程调用你的应用来传递事件消息. 比如说第一次安装完成时, 会通过以下命令来传递事件消息:
MyApp.exe --squirrel-install x.y.z.m

使用 C# 代码实现后台定时检查更新

以下代码使用 Process 启动更新程序, 并在程序中使用定时器来定期检查更新.
需要注意的是执行安装程序时, 会先将程序解压到 %LocalAppData%\SquirrelTemp 目录中, 此时 squirrel.exe 的工作目录也在此处. 在安装结束启动新进程调用 app 传递事件消息时, 子进程的工作目录默认与父进程相同. 所以在处理--squirrel-install事件时, 为了调用 squirrel.exe 来创建快捷方式, 必须指定绝对路径, 否则会找不到可执行文件.

public static async Task<int> InvokeProcessAsync(string fileName, string arguments, string workingDirectory = null) { var activity = new Activity(SquirrelDiagnosticListenerExtensions.ExecuteCommand); s_diagnosticListener.WriteStartActivity(activity, new { fileName, arguments, workingDirectory, AppDomain.CurrentDomain.BaseDirectory }); using (var process = new Process()) { process.StartInfo = new ProcessStartInfo() { // 可执行文件查找顺序: // 1. 绝对路径 // 2. Environment.ProcessPath 或 Process.GetCurrentProcess().MainModule.FileName 下查找 // 3. Directory.GetCurrentDirectory() 下查找 // 4. PATH 环境变量中查找 FileName = fileName ?? string.Empty, Arguments = arguments ?? string.Empty, UseShellExecute = false, CreateNoWindow = true, WorkingDirectory = workingDirectory ?? string.Empty,// 为空时取 Directory.GetCurrentDirectory() RedirectStandardInput = false, RedirectStandardOutput = true, RedirectStandardError = true }; process.Start(); var cts = new CancellationTokenSource(); cts.CancelAfter(1000 * 300); await process.WaitForExitAsync(cts.Token); // 设置进程超时时间 bool Timeout = false; if (!process.HasExited) { Timeout = true; process.Kill(); // 如果超时, 则强制退出进程 } var stdout = await process.StandardOutput.ReadToEndAsync(); var stderr = await process.StandardError.ReadToEndAsync(); s_diagnosticListener.WriteStopActivity(activity, new { process.ExitCode, Timeout, stdout, stderr }); return process.ExitCode; } } public void EanbleAutoUpdate() { if (_timer == null) { var timer = new System.Threading.Timer(async (object state) => await UpdateAsync(), null, Timeout.Infinite, Timeout.Infinite); Interlocked.CompareExchange(ref _timer, timer, null); } _timer.Change(TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(3)); }

在程序入口点处理 squirrel 事件

以下代码在初次安装成功后创建桌面快捷方式, 在卸载时删除快捷方式.

var AppDirectory = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..")); var SquirrelFilePath = Path.Combine(AppDirectory, "Update.exe"); var AppName = new DirectoryInfo(AppDirectory).Name; await UpdateManager.HandleSquirrelEventsAsync( async version => await UpdateManager.InvokeProcessAsync(SquirrelFilePath, $"--createShortcut {AppName}.exe", AppDirectory),//创建快捷方式 async version => await UpdateManager.InvokeProcessAsync(SquirrelFilePath, $"--removeShortcut {AppName}.exe", AppDirectory),//删除快捷方式 (version) => { MessageBox.Show($"onAppUpdate 版本:{version} 已下载完成, 请关闭应用."); return Task.CompletedTask; }, (version) => { MessageBox.Show("onAppObsoleted"); return Task.CompletedTask; }, () => { MessageBox.Show("欢迎使用本 APP !"); return Task.CompletedTask; });

HandleSquirrelEventsAsync 方法

Squirrel.SquirrelAwareApp.HandleEvents 方法用于帮助处理 Squirrel 事件. 以下代码在源码的基础上调整为异步委托, 并在委托调用失败时, 将错误信息写入 Windows Application event log, 方便调试.

/// <summary> /// 处理 Squirrel 事件 /// </summary> /// <param>在应用程序初始化安装结束时调用</param> /// <param>在应用程序更新结束时调用</param> /// <param>在应用程序不是最新版本时调用</param> /// <param>在应用程序卸载结束时调用</param> /// <param>在应用程序第一次启动时调用</param> public static async Task HandleSquirrelEventsAsync( Func<Version, Task> onInitialInstall = null, Func<Version, Task> onAppUninstall = null, Func<Version, Task> onAppUpdate = null, Func<Version, Task> onAppObsoleted = null, Func<Task> onFirstRun = null, string[] arguments = null) { Func<Version, Task> defaultBlock = v => Task.CompletedTask; var args = arguments ?? Environment.GetCommandLineArgs().Skip(1).ToArray(); if (args.Length == 0) return; var lookup = new[] { new { Key = "--squirrel-install", Value = onInitialInstall ?? defaultBlock }, new { Key = "--squirrel-updated", Value = onAppUpdate ?? defaultBlock }, new { Key = "--squirrel-obsolete", Value = onAppObsoleted ?? defaultBlock }, new { Key = "--squirrel-uninstall", Value = onAppUninstall ?? defaultBlock }, }.ToDictionary(k => k.Key, v => v.Value); if (args[0] == "--squirrel-firstrun") { await onFirstRun?.Invoke(); return; } if (args.Length != 2 || !lookup.ContainsKey(args[0]) ) { return; } try { var version = new Version(args[1]); await lookup[args[0]](version); Environment.Exit(0); } catch (Exception ex) { Environment.FailFast($"Fatal Exception Occurs When Handle Squirrel Events With Arguments '{args}'", ex); } } 6. 其他更新方案

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwwwxp.html