small code refactoring
This commit is contained in:
@@ -1,267 +1,261 @@
|
||||
using System;
|
||||
using Microsoft.VisualBasic.CompilerServices;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.IO.Pipes;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualBasic.CompilerServices;
|
||||
using Pilz.Updating.UpdateInstaller;
|
||||
|
||||
namespace Pilz.Updating
|
||||
namespace Pilz.Updating;
|
||||
|
||||
public class UpdateClient
|
||||
{
|
||||
public class UpdateClient
|
||||
|
||||
// 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();
|
||||
|
||||
// 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; }
|
||||
public ApplicationVersion CurrentVersion { get; private set; }
|
||||
public Channels MinimumChannel { get; private set; }
|
||||
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;
|
||||
|
||||
// C o n s t r u c t o r s
|
||||
|
||||
public UpdateClient(string updateUrl, ApplicationVersion currentVersion, Channels minimumChannel)
|
||||
{
|
||||
UpdateUrl = updateUrl;
|
||||
CurrentVersion = currentVersion;
|
||||
MinimumChannel = (Channels)Math.Max((int)minimumChannel, (int)currentVersion.Channel);
|
||||
}
|
||||
|
||||
// E b v e n t s
|
||||
// E v e n t M e t h o d s
|
||||
|
||||
public event UpdateStatusChangedEventHandler UpdateStatusChanged;
|
||||
private bool RaiseDownloadingUpdate(UpdatePackageInfo pkg)
|
||||
{
|
||||
var e = new CancelEventArgs(false);
|
||||
DownloadingUpdate?.Invoke(pkg, e);
|
||||
return e.Cancel;
|
||||
}
|
||||
|
||||
public delegate void UpdateStatusChangedEventHandler(UpdateStatus newStatus);
|
||||
private bool RaiseInstallingUpdate(UpdatePackageInfo pkg)
|
||||
{
|
||||
var e = new CancelEventArgs(true);
|
||||
InstallingUpdate?.Invoke(pkg, e);
|
||||
return e.Cancel;
|
||||
}
|
||||
|
||||
public event DownloadingUpdateEventHandler DownloadingUpdate;
|
||||
// U p d a t e R o u t i n e s
|
||||
|
||||
public delegate void DownloadingUpdateEventHandler(UpdatePackageInfo pkg, CancelEventArgs e);
|
||||
public async Task UpdateInteractive()
|
||||
{
|
||||
var latestVersion = await CheckForUpdate();
|
||||
if (latestVersion is null)
|
||||
NoUpdatesFound?.Invoke();
|
||||
else
|
||||
await UpdateInteractive(latestVersion);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// F i e l d s
|
||||
|
||||
private readonly Dictionary<UpdatePackageInfo, string> dicPackagePaths = new Dictionary<UpdatePackageInfo, string>();
|
||||
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; }
|
||||
public ApplicationVersion CurrentVersion { get; private set; }
|
||||
public Channels MinimumChannel { get; private set; }
|
||||
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;
|
||||
|
||||
// C o n s t r u c t o r s
|
||||
|
||||
public UpdateClient(string updateUrl, ApplicationVersion currentVersion, Channels minimumChannel)
|
||||
public async Task UpdateInteractive(UpdatePackageInfo package)
|
||||
{
|
||||
if (!RaiseDownloadingUpdate(package) && await DownloadPackageAsync(package))
|
||||
{
|
||||
UpdateUrl = updateUrl;
|
||||
CurrentVersion = currentVersion;
|
||||
MinimumChannel = (Channels)Math.Max((int)minimumChannel, (int)currentVersion.Channel);
|
||||
if (!RaiseInstallingUpdate(package))
|
||||
await InstallPackage(package);
|
||||
}
|
||||
}
|
||||
|
||||
// E v e n t M e t h o d s
|
||||
public void RaiseUpdateStatusChanged(UpdateStatus newStatus)
|
||||
{
|
||||
UpdateStatusChanged?.Invoke(newStatus);
|
||||
}
|
||||
|
||||
private bool RaiseDownloadingUpdate(UpdatePackageInfo pkg)
|
||||
// F e a t u r e s
|
||||
|
||||
public async Task<UpdateInfo> GetUpdateInfo()
|
||||
{
|
||||
string str = await WebClient.GetStringAsync(UpdateUrl);
|
||||
var info = UpdateInfo.Parse(str);
|
||||
return info;
|
||||
}
|
||||
|
||||
public async Task<UpdatePackageInfo> CheckForUpdate()
|
||||
{
|
||||
RaiseUpdateStatusChanged(UpdateStatus.Searching);
|
||||
UpdateInfo = await GetUpdateInfo();
|
||||
if (UpdateInfo is not null)
|
||||
return CheckForUpdate(UpdateInfo);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public UpdatePackageInfo CheckForUpdate(UpdateInfo updateInfo)
|
||||
{
|
||||
UpdatePackageInfo foundPkgInfo = null;
|
||||
var latestVersion = CurrentVersion;
|
||||
RaiseUpdateStatusChanged(UpdateStatus.Searching);
|
||||
foreach (UpdatePackageInfo pkgInfo in updateInfo.Packages)
|
||||
{
|
||||
var e = new CancelEventArgs(false);
|
||||
DownloadingUpdate?.Invoke(pkg, e);
|
||||
return e.Cancel;
|
||||
}
|
||||
|
||||
private bool RaiseInstallingUpdate(UpdatePackageInfo pkg)
|
||||
{
|
||||
var e = new CancelEventArgs(true);
|
||||
InstallingUpdate?.Invoke(pkg, e);
|
||||
return e.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
|
||||
await UpdateInteractive(latestVersion);
|
||||
}
|
||||
|
||||
public async Task UpdateInteractive(UpdatePackageInfo package)
|
||||
{
|
||||
if (!RaiseDownloadingUpdate(package) && await DownloadPackageAsync(package))
|
||||
if (pkgInfo.Version.Channel <= MinimumChannel && pkgInfo.Version > latestVersion)
|
||||
{
|
||||
if (!RaiseInstallingUpdate(package))
|
||||
await InstallPackage(package);
|
||||
foundPkgInfo = pkgInfo;
|
||||
latestVersion = pkgInfo.Version;
|
||||
}
|
||||
}
|
||||
|
||||
public void RaiseUpdateStatusChanged(UpdateStatus newStatus)
|
||||
UpdatePackageInfo = foundPkgInfo;
|
||||
return foundPkgInfo;
|
||||
}
|
||||
|
||||
public async Task<bool> DownloadPackageAsync(UpdatePackageInfo package)
|
||||
{
|
||||
curDownloadingStatus = UpdateStatus.DownloadingPackage;
|
||||
RaiseUpdateStatusChanged(curDownloadingStatus);
|
||||
string dirPath = Path.Combine(MyPaths.GetMyAppDataPath(), Conversions.ToString(package.GetHashCode()));
|
||||
string zipPath = Path.Combine(dirPath, PackageFileNameDefinations.ZIP_PACKAGE_FILENAME);
|
||||
var dir = new DirectoryInfo(dirPath);
|
||||
try
|
||||
{
|
||||
UpdateStatusChanged?.Invoke(newStatus);
|
||||
// Ensure existing and empty directory for the Zip File
|
||||
if (dir.Exists)
|
||||
dir.Delete(true);
|
||||
|
||||
dir.Create();
|
||||
|
||||
// Download zip package
|
||||
using var zipFile = new FileStream(zipPath, FileMode.Create, FileAccess.ReadWrite);
|
||||
using var zipStream = await WebClient.GetStreamAsync(package.Packagelink);
|
||||
await zipStream.CopyToAsync(zipFile);
|
||||
|
||||
// Remember path to package directory
|
||||
dicPackagePaths.Add(package, dirPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// F e a t u r e s
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<UpdateInfo> GetUpdateInfo()
|
||||
private async Task<FileInfo> DownloadUpdateInstaller()
|
||||
{
|
||||
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))
|
||||
{
|
||||
string str = await WebClient.GetStringAsync(UpdateUrl);
|
||||
var info = UpdateInfo.Parse(str);
|
||||
return info;
|
||||
using var installerZipStream = await WebClient.GetStreamAsync(UpdateInfo.UpdateInstallerLink);
|
||||
await installerZipStream.CopyToAsync(installerZipFile);
|
||||
}
|
||||
|
||||
public async Task<UpdatePackageInfo> CheckForUpdate()
|
||||
// Extract update installer
|
||||
var installerExtractPath = installerDirPath.CreateSubdirectory("extracted");
|
||||
ZipFile.ExtractToDirectory(installerZipPath, installerExtractPath.FullName);
|
||||
File.Delete(installerZipPath);
|
||||
|
||||
// Get UpdateInstaller.exe file
|
||||
return installerExtractPath.EnumerateFiles("*.exe").FirstOrDefault();
|
||||
}
|
||||
|
||||
private void StartUpdateInstaller(string packagePath, string installerPath)
|
||||
{
|
||||
RaiseUpdateStatusChanged(UpdateStatus.StartingInstaller);
|
||||
|
||||
// Create update settings
|
||||
var myAppPath = IO.Extensions.GetExecutablePath();
|
||||
var updateConfig = new UpdateInstallerConfig
|
||||
{
|
||||
RaiseUpdateStatusChanged(UpdateStatus.Searching);
|
||||
UpdateInfo = await GetUpdateInfo();
|
||||
if (UpdateInfo is not null)
|
||||
return CheckForUpdate(UpdateInfo);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
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
|
||||
};
|
||||
|
||||
public UpdatePackageInfo CheckForUpdate(UpdateInfo updateInfo)
|
||||
// Start UpdateInstaller
|
||||
var procStartInfo = new ProcessStartInfo
|
||||
{
|
||||
UpdatePackageInfo foundPkgInfo = null;
|
||||
var latestVersion = CurrentVersion;
|
||||
RaiseUpdateStatusChanged(UpdateStatus.Searching);
|
||||
foreach (UpdatePackageInfo pkgInfo in updateInfo.Packages)
|
||||
{
|
||||
if (pkgInfo.Version.Channel <= MinimumChannel && pkgInfo.Version > latestVersion)
|
||||
{
|
||||
foundPkgInfo = pkgInfo;
|
||||
latestVersion = pkgInfo.Version;
|
||||
}
|
||||
}
|
||||
FileName = installerPath,
|
||||
Arguments = updateConfig.ToString(),
|
||||
UseShellExecute = false,
|
||||
Verb = InstallAsAdmin ? "runas" : string.Empty
|
||||
};
|
||||
Process.Start(procStartInfo);
|
||||
UpdateInstallerStarted?.Invoke();
|
||||
}
|
||||
|
||||
UpdatePackageInfo = foundPkgInfo;
|
||||
return foundPkgInfo;
|
||||
}
|
||||
|
||||
public async Task<bool> DownloadPackageAsync(UpdatePackageInfo package)
|
||||
public async Task<bool> InstallPackage(UpdatePackageInfo package)
|
||||
{
|
||||
if (dicPackagePaths.TryGetValue(package, out var packagePath))
|
||||
{
|
||||
curDownloadingStatus = UpdateStatus.DownloadingPackage;
|
||||
RaiseUpdateStatusChanged(curDownloadingStatus);
|
||||
string dirPath = Path.Combine(MyPaths.GetMyAppDataPath(), Conversions.ToString(package.GetHashCode()));
|
||||
string zipPath = Path.Combine(dirPath, PackageFileNameDefinations.ZIP_PACKAGE_FILENAME);
|
||||
var dir = new DirectoryInfo(dirPath);
|
||||
try
|
||||
{
|
||||
// Ensure existing and empty directory for the Zip File
|
||||
if (dir.Exists)
|
||||
dir.Delete(true);
|
||||
// Download update installer
|
||||
var installerPath = await DownloadUpdateInstaller();
|
||||
|
||||
dir.Create();
|
||||
// Start update installer
|
||||
StartUpdateInstaller(packagePath, installerPath.FullName);
|
||||
|
||||
// Download zip package
|
||||
using var zipFile = new FileStream(zipPath, FileMode.Create, FileAccess.ReadWrite);
|
||||
using var zipStream = await WebClient.GetStreamAsync(package.Packagelink);
|
||||
await zipStream.CopyToAsync(zipFile);
|
||||
|
||||
// Remember path to package directory
|
||||
dicPackagePaths.Add(package, dirPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Close Host Application
|
||||
if (AutoCloseHostApplication)
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<FileInfo> DownloadUpdateInstaller()
|
||||
{
|
||||
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))
|
||||
{
|
||||
using var installerZipStream = await WebClient.GetStreamAsync(UpdateInfo.UpdateInstallerLink);
|
||||
await installerZipStream.CopyToAsync(installerZipFile);
|
||||
}
|
||||
|
||||
// Extract update installer
|
||||
var installerExtractPath = installerDirPath.CreateSubdirectory("extracted");
|
||||
ZipFile.ExtractToDirectory(installerZipPath, installerExtractPath.FullName);
|
||||
File.Delete(installerZipPath);
|
||||
|
||||
// Get UpdateInstaller.exe file
|
||||
return installerExtractPath.EnumerateFiles("*.exe").FirstOrDefault();
|
||||
}
|
||||
|
||||
private void StartUpdateInstaller(string packagePath, string installerPath)
|
||||
{
|
||||
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 false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user