rework client and remove separated update installer (gui not finished yet)

This commit is contained in:
2024-06-26 17:24:08 +02:00
parent 5bc0c374cf
commit 07853467b1
68 changed files with 681 additions and 1462 deletions

View File

@@ -1,97 +1,72 @@
using Pilz.Updating.UpdateInstaller;
using System.ComponentModel;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.IO.Compression;
using System.Runtime.CompilerServices;
using System.Security.Principal;
using Z.IO.Extensions;
namespace Pilz.Updating;
namespace Pilz.Updating.Client;
public class UpdateClient(string updateUrl, ApplicationVersion currentVersion, Channels minimumChannel)
public class UpdateClient(string updateUrl, AppVersion currentVersion, Channels minimumChannel)
{
// E v e n t s
// E b v e n t s
public event UpdateStatusChangedEventHandler UpdateStatusChanged;
public delegate void UpdateStatusChangedEventHandler(UpdateStatus newStatus);
public event DownloadingUpdateEventHandler DownloadingUpdate;
public delegate void DownloadingUpdateEventHandler(UpdatePackageInfo pkg, CancelEventArgs e);
public event InstallingUpdateEventHandler InstallingUpdate;
public delegate void InstallingUpdateEventHandler(UpdatePackageInfo pkg, CancelEventArgs e);
public event UpdateInstallerStartedEventHandler UpdateInstallerStarted;
public delegate void UpdateInstallerStartedEventHandler();
public event NoUpdatesFoundEventHandler NoUpdatesFound;
public delegate void NoUpdatesFoundEventHandler();
public event UpdateClientStatusChangedEventHandler OnStatusChanged;
// F i e l d s
private readonly Dictionary<UpdatePackageInfo, string> dicPackagePaths = [];
private UpdateStatus curDownloadingStatus = UpdateStatus.Waiting;
// P r o p e r t i e s
public HttpClient WebClient { get; private set; } = new();
public string UpdateUrl { get; private set; } = updateUrl;
public ApplicationVersion CurrentVersion { get; private set; } = currentVersion;
public AppVersion CurrentVersion { get; private set; } = currentVersion;
public Channels MinimumChannel { get; private set; } = (Channels)Math.Max((int)minimumChannel, (int)currentVersion.Channel);
public UpdateInfo UpdateInfo { get; private set; } = null;
public UpdatePackageInfo UpdatePackageInfo { get; private set; } = null;
public bool AutoCloseHostApplication { get; set; } = false;
public bool AutoRestartHostApplication { get; set; } = false;
public string RestartHostApplicationArguments { get; set; }
public string HostApplicationPath { get; set; } = string.Empty;
public string ApplicationName { get; set; } = string.Empty;
public bool InstallAsAdmin { get; set; } = false;
public uint MillisecondsToWaitForHostApplicationToClose { get; set; } = 10000;
public bool ForceClosingHostApplication { get; set; } = true;
public bool UIDarkMode { get; set; } = false;
public bool HasUpdates => UpdatePackageInfo != null;
// E v e n t M e t h o d s
private bool RaiseDownloadingUpdate(UpdatePackageInfo pkg)
private void RaiseStatusChanged(UpdateStatus status)
{
var e = new CancelEventArgs(false);
DownloadingUpdate?.Invoke(pkg, e);
return e.Cancel;
RaiseStatusChanged(status, UpdateStatusEvent.Default);
}
private bool RaiseInstallingUpdate(UpdatePackageInfo pkg)
private void RaiseStatusChanged(UpdateStatus status, UpdateStatusEvent statusEvent)
{
var e = new CancelEventArgs(true);
InstallingUpdate?.Invoke(pkg, e);
return e.Cancel;
RaiseStatusChanged(status, statusEvent, false);
}
// U p d a t e R o u t i n e s
private bool RaiseStatusChanged(UpdateStatus status, UpdateStatusEvent statusEvent, bool canCancel)
{
var args = new UpdateStatusEventArgs(this, status, statusEvent, canCancel);
OnStatusChanged?.Invoke(this, args);
return args.CanCancel && args.Cancel;
}
// U p d a t e r o u t i n e s
public async Task UpdateInteractive()
{
var latestVersion = await CheckForUpdate();
if (latestVersion is null)
NoUpdatesFound?.Invoke();
else
if (HasUpdates)
await UpdateInteractive(latestVersion);
}
public async Task UpdateInteractive(UpdatePackageInfo package)
{
if (!RaiseDownloadingUpdate(package) && await DownloadPackageAsync(package))
{
if (!RaiseInstallingUpdate(package))
await InstallPackage(package);
}
}
public void RaiseUpdateStatusChanged(UpdateStatus newStatus)
{
UpdateStatusChanged?.Invoke(newStatus);
if (await DownloadPackageAsync(package))
await InstallPackageAsync(package);
}
// F e a t u r e s
@@ -103,21 +78,23 @@ public class UpdateClient(string updateUrl, ApplicationVersion currentVersion, C
return info;
}
public async Task<UpdatePackageInfo> CheckForUpdate()
private async Task<UpdatePackageInfo> CheckForUpdate()
{
RaiseUpdateStatusChanged(UpdateStatus.Searching);
RaiseStatusChanged(UpdateStatus.Searching, UpdateStatusEvent.PreEvent);
UpdateInfo = await GetUpdateInfo();
if (UpdateInfo is not null)
return CheckForUpdate(UpdateInfo);
else
if (UpdateInfo is null)
return null;
return CheckForUpdate(UpdateInfo);
}
public UpdatePackageInfo CheckForUpdate(UpdateInfo updateInfo)
private UpdatePackageInfo CheckForUpdate(UpdateInfo updateInfo)
{
UpdatePackageInfo foundPkgInfo = null;
var latestVersion = CurrentVersion;
RaiseUpdateStatusChanged(UpdateStatus.Searching);
foreach (UpdatePackageInfo pkgInfo in updateInfo.Packages)
{
if (pkgInfo.Version.Channel <= MinimumChannel && pkgInfo.Version > latestVersion)
@@ -128,13 +105,20 @@ public class UpdateClient(string updateUrl, ApplicationVersion currentVersion, C
}
UpdatePackageInfo = foundPkgInfo;
if (!RaiseStatusChanged(UpdateStatus.Searching, UpdateStatusEvent.PostEvent, true))
{
UpdatePackageInfo = null;
return null;
}
return foundPkgInfo;
}
public async Task<bool> DownloadPackageAsync(UpdatePackageInfo package)
{
curDownloadingStatus = UpdateStatus.DownloadingPackage;
RaiseUpdateStatusChanged(curDownloadingStatus);
RaiseStatusChanged(UpdateStatus.Downloading, UpdateStatusEvent.PreEvent);
var dirPath = Path.Combine(MyPaths.GetMyAppDataPath(), package.GetHashCode().ToString());
var zipPath = Path.Combine(dirPath, "package.zip");
var dir = new DirectoryInfo(dirPath);
@@ -159,88 +143,57 @@ public class UpdateClient(string updateUrl, ApplicationVersion currentVersion, C
return false;
}
if (!RaiseStatusChanged(UpdateStatus.Downloading, UpdateStatusEvent.PostEvent, true))
{
RaiseStatusChanged(UpdateStatus.Canceled);
return false;
}
return true;
}
private async Task<FileInfo> DownloadUpdateInstaller()
private void InstallPackage(UpdatePackageInfo package)
{
curDownloadingStatus = UpdateStatus.DownloadingInstaller;
RaiseUpdateStatusChanged(curDownloadingStatus);
// Ensure update installer path is empty
var installerDirPath = new DirectoryInfo(Path.Combine(MyPaths.GetMyAppDataPath(), "UpdateInstallerTool"));
if (installerDirPath.Exists)
installerDirPath.Delete(true);
await Task.Delay(100);
installerDirPath.Create();
await Task.Delay(100);
// Download update installer zip
var installerZipPath = Path.Combine(installerDirPath.FullName, "UpdatenInstaller.zip");
using (var installerZipFile = new FileStream(installerZipPath, FileMode.Create, FileAccess.ReadWrite))
// Extract Package
if (!RaiseStatusChanged(UpdateStatus.Extracting, UpdateStatusEvent.PreEvent, true))
{
using var installerZipStream = await WebClient.GetStreamAsync(UpdateInfo.UpdateInstallerLink);
await installerZipStream.CopyToAsync(installerZipFile);
RaiseStatusChanged(UpdateStatus.Canceled);
return;
}
if (!dicPackagePaths.TryGetValue(package, out var packagePath))
{
RaiseStatusChanged(UpdateStatus.Failed);
return;
}
var dataPath = packagePath + ".extracted";
var packagePathDir = new DirectoryInfo(packagePath);
if (packagePathDir.Exists)
{
packagePathDir.Delete(true);
Task.Delay(1000);
}
ZipFile.ExtractToDirectory(packagePath, dataPath);
RaiseStatusChanged(UpdateStatus.Extracting, UpdateStatusEvent.PostEvent);
// Extract update installer
var installerExtractPath = installerDirPath.CreateSubdirectory("extracted");
ZipFile.ExtractToDirectory(installerZipPath, installerExtractPath.FullName);
File.Delete(installerZipPath);
// Install Package
RaiseStatusChanged(UpdateStatus.Copying, UpdateStatusEvent.PreEvent);
var dataPathDir = new DirectoryInfo(dataPath);
var destDir = new DirectoryInfo(HostApplicationPath);
General.CopyFiles(dataPathDir, destDir);
RaiseStatusChanged(UpdateStatus.Copying, UpdateStatusEvent.PostEvent);
// Get UpdateInstaller.exe file
return installerExtractPath.EnumerateFiles("*.exe").FirstOrDefault();
// Delete Package
RaiseStatusChanged(UpdateStatus.Cleanup, UpdateStatusEvent.PreEvent);
File.Delete(packagePath);
Directory.Delete(dataPath, true);
RaiseStatusChanged(UpdateStatus.Cleanup, UpdateStatusEvent.PostEvent);
// Finish
RaiseStatusChanged(UpdateStatus.Done, UpdateStatusEvent.Default);
}
private void StartUpdateInstaller(string packagePath, string installerPath)
public Task InstallPackageAsync(UpdatePackageInfo package)
{
RaiseUpdateStatusChanged(UpdateStatus.StartingInstaller);
// Create update settings
var myAppPath = IO.Extensions.GetExecutablePath();
var updateConfig = new UpdateInstallerConfig
{
PackagePath = packagePath,
RestartHostApplication = AutoRestartHostApplication,
RestartHostApplicationArguments = AutoRestartHostApplication ? RestartHostApplicationArguments : string.Empty,
ApplicationName = ApplicationName,
HostApplicationPath = string.IsNullOrEmpty(HostApplicationPath) ? Path.GetDirectoryName(myAppPath) : HostApplicationPath,
HostApplicationProcessPath = myAppPath,
MillisecondsToWaitForHostApplicationToClose = MillisecondsToWaitForHostApplicationToClose,
ForceClosingHostApplication = ForceClosingHostApplication,
UIDarkMode = UIDarkMode
};
// Start UpdateInstaller
var procStartInfo = new ProcessStartInfo
{
FileName = installerPath,
Arguments = updateConfig.ToString(),
UseShellExecute = false,
Verb = InstallAsAdmin ? "runas" : string.Empty
};
Process.Start(procStartInfo);
UpdateInstallerStarted?.Invoke();
}
public async Task<bool> InstallPackage(UpdatePackageInfo package)
{
if (dicPackagePaths.TryGetValue(package, out var packagePath))
{
// Download update installer
var installerPath = await DownloadUpdateInstaller();
// Start update installer
StartUpdateInstaller(packagePath, installerPath.FullName);
// Close Host Application
if (AutoCloseHostApplication)
Environment.Exit(Environment.ExitCode);
return true;
}
return false;
return Task.Run(() => InstallPackage(package));
}
}