From e16fd63c0a677d0c0b6f75556ee6d9876377ebc4 Mon Sep 17 00:00:00 2001 From: Martin Renner Date: Fri, 13 Sep 2024 22:19:38 +0200 Subject: [PATCH] Installer improvement, Installer checks for SimHub plugin Issue #134 --- .../Actions/AbstractInstallerAction.cs | 18 ++++--- .../Actions/IInstallerAction.cs | 28 ++++++++-- .../Actions/StartStreamDeckSoftware.cs | 10 ++-- .../Actions/VerifySimHubPlugin.cs | 52 +++++++++++++++++++ StreamDeckSimHub.Installer/MainWindow.xaml | 33 ++++++------ .../MainWindowViewModel.cs | 41 +++++++++++++-- .../Tools/Configuration.cs | 9 ++++ 7 files changed, 155 insertions(+), 36 deletions(-) create mode 100644 StreamDeckSimHub.Installer/Actions/VerifySimHubPlugin.cs diff --git a/StreamDeckSimHub.Installer/Actions/AbstractInstallerAction.cs b/StreamDeckSimHub.Installer/Actions/AbstractInstallerAction.cs index 77d988e..17557ea 100644 --- a/StreamDeckSimHub.Installer/Actions/AbstractInstallerAction.cs +++ b/StreamDeckSimHub.Installer/Actions/AbstractInstallerAction.cs @@ -16,26 +16,30 @@ public abstract partial class AbstractInstallerAction : ObservableObject, IInsta private string _message = string.Empty; [ObservableProperty] - private Brush _actionState = ActionStates.InactiveBrush; + private Brush _actionResultColor = ActionColors.InactiveBrush; public async Task Execute() { _logger.Info($"Starting action {GetType().Name}"); - ActionState = ActionStates.RunningBrush; + ActionResultColor = ActionColors.RunningBrush; try { var result = await ExecuteInternal(); - ActionState = result switch + ActionResultColor = result switch { - ActionResult.Success => ActionStates.SuccessBrush, - ActionResult.Error => ActionStates.ErrorBrush, - ActionResult.NotRequired => ActionStates.InactiveBrush, - _ => ActionState + ActionResult.Success => ActionColors.SuccessBrush, + ActionResult.Error => ActionColors.ErrorBrush, + ActionResult.NotRequired => ActionColors.InactiveBrush, + ActionResult.Warning => ActionColors.WarningBrush, + _ => throw new ArgumentOutOfRangeException() }; _logger.Info($"Finished action {GetType().Name} with result {result}"); + + // Give the user a tiny moment to realize that there is something going on. await Task.Delay(1000); + return result; } catch (Exception e) diff --git a/StreamDeckSimHub.Installer/Actions/IInstallerAction.cs b/StreamDeckSimHub.Installer/Actions/IInstallerAction.cs index e362392..ba0f28a 100644 --- a/StreamDeckSimHub.Installer/Actions/IInstallerAction.cs +++ b/StreamDeckSimHub.Installer/Actions/IInstallerAction.cs @@ -7,23 +7,45 @@ namespace StreamDeckSimHub.Installer.Actions; public enum ActionResult { - Success, NotRequired, + Success, + Warning, Error, } -public abstract class ActionStates +public abstract class ActionColors { public static readonly Brush InactiveBrush = Brushes.Gray; public static readonly Brush RunningBrush = Brushes.Orange; public static readonly Brush SuccessBrush = Brushes.Green; public static readonly Brush ErrorBrush = Brushes.Red; + public static readonly Brush WarningBrush = Brushes.Yellow; } +/// +/// An action that can be executed by the installer. +/// +/// Holds the action logic, but is also misused as a ViewModel. public interface IInstallerAction { + /// + /// Displayed as header in the UI. + /// string Name { get; } + + /// + /// Detailed message below the header. + /// string Message { get; } - Brush ActionState { get; } + + /// + /// Result of the action in the form of a colored brush. + /// + Brush ActionResultColor { get; } + + /// + /// Executes the action. + /// + /// matic result of the action. Is used in the installer logic. Task Execute(); } diff --git a/StreamDeckSimHub.Installer/Actions/StartStreamDeckSoftware.cs b/StreamDeckSimHub.Installer/Actions/StartStreamDeckSoftware.cs index 61d51f8..cb00c27 100644 --- a/StreamDeckSimHub.Installer/Actions/StartStreamDeckSoftware.cs +++ b/StreamDeckSimHub.Installer/Actions/StartStreamDeckSoftware.cs @@ -16,16 +16,16 @@ public class StartStreamDeckSoftware : AbstractInstallerAction protected override Task ExecuteInternal() { - var installDir = GetStreamDeckInstallationPath(); - ProcessTools.StartProcess(Path.Combine(installDir, "StreamDeck.exe"), installDir); + var installFolder = GetStreamDeckInstallFolder(); + ProcessTools.StartProcess(Path.Combine(installFolder, "StreamDeck.exe"), installFolder); SetAndLogInfo("Stream Deck software started"); return Task.FromResult(ActionResult.Success); } - private string GetStreamDeckInstallationPath() + private string GetStreamDeckInstallFolder() { - var installPath = (string?) Registry.GetValue(Configuration.StreamDeckRegistryFolder, "InstallDir", null); + var installPath = (string?) Registry.GetValue(Configuration.StreamDeckRegistryFolder, Configuration.StreamDeckRegistryInstallFolder, null); if (!string.IsNullOrEmpty(installPath)) { SetAndLogInfo($"Found Stream Deck directory in registry: {installPath}"); @@ -33,6 +33,6 @@ private string GetStreamDeckInstallationPath() } SetAndLogInfo($"Could not find Stream Deck directory in registry. Using default."); - return Path.Combine("C:", "Program Files", "Elgato", "StreamDeck"); + return Configuration.StreamDeckDefaultInstallFolder; } } diff --git a/StreamDeckSimHub.Installer/Actions/VerifySimHubPlugin.cs b/StreamDeckSimHub.Installer/Actions/VerifySimHubPlugin.cs new file mode 100644 index 0000000..af97ff9 --- /dev/null +++ b/StreamDeckSimHub.Installer/Actions/VerifySimHubPlugin.cs @@ -0,0 +1,52 @@ +using System.Diagnostics; +using System.IO; +using Microsoft.Win32; +using StreamDeckSimHub.Installer.Tools; + +namespace StreamDeckSimHub.Installer.Actions; + +public class VerifySimHubPlugin : AbstractInstallerAction +{ + public override string Name => "Verify SimHub installation and SimHub Property Server plugin"; + + protected override Task ExecuteInternal() + { + var installFolder = GetSimHubInstallFolder(); + if (!File.Exists(Path.Combine(installFolder, "SimHubWPF.exe"))) + { + SetAndLogInfo("Could not find SimHub installation. SimHub is required for this plugin."); + return Task.FromResult(ActionResult.Warning); + } + + if (!File.Exists(Path.Combine(installFolder, "PropertyServer.dll"))) + { + SetAndLogInfo($"Could not find SimHub Property Server plugin. Is is required for this plugin. Please install it from {Configuration.SimHubPluginUrl}"); + return Task.FromResult(ActionResult.Warning); + } + + var pluginVersionInfo = FileVersionInfo.GetVersionInfo(Path.Combine(installFolder, "PropertyServer.dll")); + Version pluginVersion = new(pluginVersionInfo.ProductMajorPart, pluginVersionInfo.ProductMinorPart, pluginVersionInfo.ProductBuildPart); + if (pluginVersion < Configuration.RequiredSimHubPluginVersion) + { + SetAndLogInfo($"SimHub Property Server plugin is too old. Found version {pluginVersion}, required version is {Configuration.RequiredSimHubPluginVersion}. Please update the SimHub Property Server plugin by visiting {Configuration.SimHubPluginUrl}"); + return Task.FromResult(ActionResult.Warning); + } + + SetAndLogInfo("Found SimHub and the SimHub Property Server plugin."); + return Task.FromResult(ActionResult.Success); + } + + private string GetSimHubInstallFolder() + { + var installPath = (string?) Registry.GetValue(Configuration.SimHubRegistryFolder, Configuration.SimHubRegistryInstallFolder, null); + if (!string.IsNullOrEmpty(installPath)) + { + SetAndLogInfo($"Found SimHub directory in registry: {installPath}"); + return installPath; + } + + SetAndLogInfo($"Could not find SimHub directory in registry. Using default."); + return Configuration.StreamDeckDefaultInstallFolder; + } + +} diff --git a/StreamDeckSimHub.Installer/MainWindow.xaml b/StreamDeckSimHub.Installer/MainWindow.xaml index 66b2ac7..9f3516a 100644 --- a/StreamDeckSimHub.Installer/MainWindow.xaml +++ b/StreamDeckSimHub.Installer/MainWindow.xaml @@ -6,7 +6,7 @@ xmlns:local="clr-namespace:StreamDeckSimHub.Installer" xmlns:localActions="clr-namespace:StreamDeckSimHub.Installer.Actions" mc:Ignorable="d" - Title="Stream Deck SimHub Plugin Installer" MinHeight="500" Width="600" SizeToContent="Height"> + Title="Stream Deck SimHub Plugin Installer" MinHeight="580" Width="600" SizeToContent="Height"> @@ -37,24 +37,25 @@ - - - + + + + - - - - - - - - - + + + + + + + + + @@ -62,7 +63,7 @@ - + diff --git a/StreamDeckSimHub.Installer/MainWindowViewModel.cs b/StreamDeckSimHub.Installer/MainWindowViewModel.cs index c479e2b..7292aeb 100644 --- a/StreamDeckSimHub.Installer/MainWindowViewModel.cs +++ b/StreamDeckSimHub.Installer/MainWindowViewModel.cs @@ -13,6 +13,7 @@ public partial class MainWindowViewModel : ObservableObject { private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); private static readonly Brush SuccessBrush = Brushes.Green; + private static readonly Brush WarningBrush = Brushes.Orange; private static readonly Brush ErrorBrush = Brushes.Red; public string Version => ThisAssembly.AssemblyFileVersion; @@ -41,17 +42,41 @@ private async Task Install() return; } - var result = true; + var result = ActionResult.Success; var installStreamDeckPlugin = new InstallStreamDeckPlugin(); InstallerSteps.Add(installStreamDeckPlugin); - result &= await installStreamDeckPlugin.Execute() != ActionResult.Error; + result = SetHigherResultLevel(await installStreamDeckPlugin.Execute(), result); var startStreamDeck = new StartStreamDeckSoftware(); InstallerSteps.Add(startStreamDeck); - result &= await startStreamDeck.Execute() != ActionResult.Error; + result = SetHigherResultLevel(await startStreamDeck.Execute(), result); - if (result) SetSuccessResultText(); - else SetErrorResultText(); + var verifySimHubPlugin = new VerifySimHubPlugin(); + InstallerSteps.Add(verifySimHubPlugin); + result = SetHigherResultLevel(await verifySimHubPlugin.Execute(), result); + + switch (result) + { + case ActionResult.NotRequired: + case ActionResult.Success: + SetSuccessResultText(); + break; + case ActionResult.Warning: + SetWarningResultText(); + break; + case ActionResult.Error: + SetErrorResultText(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private ActionResult SetHigherResultLevel(ActionResult result, ActionResult existingResult) + { + var v1 = (int)result; + var v2 = (int)existingResult; + return v1 > v2 ? result : existingResult; } private void ClearResultText() @@ -66,6 +91,12 @@ private void SetSuccessResultText() ResultBrush = SuccessBrush; } + private void SetWarningResultText() + { + Result = "There have been warnings during the installation. Please check the steps above."; + ResultBrush = WarningBrush; + } + private void SetErrorResultText() { Result = """ diff --git a/StreamDeckSimHub.Installer/Tools/Configuration.cs b/StreamDeckSimHub.Installer/Tools/Configuration.cs index e001bd0..4afce59 100644 --- a/StreamDeckSimHub.Installer/Tools/Configuration.cs +++ b/StreamDeckSimHub.Installer/Tools/Configuration.cs @@ -11,10 +11,19 @@ public static class Configuration public const string PluginProcessName = "StreamDeckSimHub"; public const string StreamDeckRegistryFolder = @"HKEY_CURRENT_USER\SOFTWARE\Elgato Systems GmbH\StreamDeck"; + public const string StreamDeckRegistryInstallFolder = "InstallDir"; + public static readonly string StreamDeckDefaultInstallFolder = Path.Combine("C:", "Program Files", "Elgato", "StreamDeck"); public static readonly string AppDataRoaming = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); public static readonly string StreamDeckPluginDir = Path.Combine(AppDataRoaming, "Elgato", "StreamDeck", "Plugins"); public const string PluginDirName = "net.planetrenner.simhub.sdPlugin"; public const string PluginZipName = "net.planetrenner.simhub.streamDeckPlugin"; + + public const string SimHubRegistryFolder = @"HKEY_CURRENT_USER\Software\SimHub"; + public const string SimHubRegistryInstallFolder = "InstallDirectory"; + public static readonly string SimHubDefaultInstallFolder = Path.Combine("C:", "Program Files (x86)", "SimHub"); + + public const string SimHubPluginUrl = "https://github.com/pre-martin/SimHubPropertyServer"; + public static readonly Version RequiredSimHubPluginVersion = new(1, 9, 6); }