diff --git a/Agent.Installer.Win/Agent.Installer.Win.csproj b/Agent.Installer.Win/Agent.Installer.Win.csproj index e08b3dc1..a3df7ba2 100644 --- a/Agent.Installer.Win/Agent.Installer.Win.csproj +++ b/Agent.Installer.Win/Agent.Installer.Win.csproj @@ -50,10 +50,12 @@ + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.2\System.Runtime.Serialization.dll + @@ -72,10 +74,18 @@ MSBuild:Compile Designer - + + Models\ConnectionInfo.cs + + + Models\InstallerSettings.cs + + + + diff --git a/Agent.Installer.Win/App.xaml b/Agent.Installer.Win/App.xaml index 459d3733..aa4b0db2 100644 --- a/Agent.Installer.Win/App.xaml +++ b/Agent.Installer.Win/App.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Remotely.Agent.Installer.Win" - StartupUri="MainWindow.xaml" Startup="App_Startup"> + StartupUri="MainWindow.xaml"> diff --git a/Agent.Installer.Win/App.xaml.cs b/Agent.Installer.Win/App.xaml.cs index 482fc9ce..370b45ca 100644 --- a/Agent.Installer.Win/App.xaml.cs +++ b/Agent.Installer.Win/App.xaml.cs @@ -17,24 +17,6 @@ namespace Remotely.Agent.Installer.Win /// public partial class App : Application { - public void App_Startup(object sender, StartupEventArgs e) - { - if (e.Args.Contains("-uninstall")) - { - try - { - Logger.Write("Uninstall command received. Preparing temp directory."); - var targetPath = Path.Combine(Path.GetTempPath(), "Remotely_Installer.exe"); - File.Copy(Assembly.GetExecutingAssembly().Location, targetPath, true); - Logger.Write("Launching uninstaller."); - Process.Start(targetPath, "-rununinstall"); - Environment.Exit(0); - } - catch (Exception ex) - { - Logger.Write(ex); - } - } - } + } } diff --git a/Agent.Installer.Win/MainWindow.xaml b/Agent.Installer.Win/MainWindow.xaml index 08c2036f..c5e674c0 100644 --- a/Agent.Installer.Win/MainWindow.xaml +++ b/Agent.Installer.Win/MainWindow.xaml @@ -7,9 +7,11 @@ xmlns:local="clr-namespace:Remotely.Agent.Installer.Win" mc:Ignorable="d" WindowStyle="None" - ResizeMode="NoResize" + ResizeMode="CanResizeWithGrip" + AllowsTransparency="True" MouseLeftButtonDown="Window_MouseLeftButtonDown" Loaded="Window_Loaded" + WindowStartupLocation="CenterScreen" Title="Remotely Installer" Height="325" Width="500" Icon="Assets/favicon.ico"> @@ -36,17 +38,25 @@ - - Provider: - - - - Server URL: - - - - - + + + + + + + + + + + + + + + + + + + @@ -70,6 +80,7 @@ HorizontalAlignment="Right" Style="{StaticResource NormalButton}" Margin="5,0,0,0" + IsEnabled="{Binding IsReadyState}" Visibility="{Binding IsServiceMissing, Converter={StaticResource BooleanToVisibilityConverter}}" Command="{Binding InstallCommand}"> Install @@ -78,6 +89,7 @@ HorizontalAlignment="Right" Style="{StaticResource NormalButton}" Margin="5,0,0,0" + IsEnabled="{Binding IsReadyState}" Visibility="{Binding IsServiceInstalled, Converter={StaticResource BooleanToVisibilityConverter}}" Command="{Binding UninstallCommand}"> Uninstall diff --git a/Agent.Installer.Win/MainWindow.xaml.cs b/Agent.Installer.Win/MainWindow.xaml.cs index ca5ea623..94e808ba 100644 --- a/Agent.Installer.Win/MainWindow.xaml.cs +++ b/Agent.Installer.Win/MainWindow.xaml.cs @@ -1,4 +1,5 @@ -using Remotely.Agent.Installer.Win.ViewModels; +using Remotely.Agent.Installer.Win.Services; +using Remotely.Agent.Installer.Win.ViewModels; using System; using System.Collections.Generic; using System.Linq; @@ -23,6 +24,12 @@ namespace Remotely.Agent.Installer.Win { public MainWindow() { + if (CommandLineParser.CommandLineArgs.ContainsKey("quiet")) + { + Hide(); + ShowInTaskbar = false; + _ = new MainWindowViewModel().Init(); + } InitializeComponent(); } @@ -31,9 +38,9 @@ namespace Remotely.Agent.Installer.Win DragMove(); } - private void Window_Loaded(object sender, RoutedEventArgs e) + private async void Window_Loaded(object sender, RoutedEventArgs e) { - (DataContext as MainWindowViewModel).Init(); + await (DataContext as MainWindowViewModel).Init(); } private void CloseButton_Click(object sender, RoutedEventArgs e) diff --git a/Agent.Installer.Win/Models/InstallerSettings.cs b/Agent.Installer.Win/Models/InstallerSettings.cs deleted file mode 100644 index 9d37316f..00000000 --- a/Agent.Installer.Win/Models/InstallerSettings.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; - -namespace Remotely.Agent.Installer.Win -{ - [DataContract] - public class InstallerSettings - { - [DataMember] - public string OrganizationID { get; set; } - - [DataMember] - public string OrganizationName { get; set; } - - [DataMember] - public string ServerUrl { get; set; } - } -} diff --git a/Agent.Installer.Win/Services/CommandLineParser.cs b/Agent.Installer.Win/Services/CommandLineParser.cs new file mode 100644 index 00000000..454776f6 --- /dev/null +++ b/Agent.Installer.Win/Services/CommandLineParser.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace Remotely.Agent.Installer.Win.Services +{ + public class CommandLineParser + { + private static Dictionary commandLineArgs; + public static Dictionary CommandLineArgs + { + get + { + if (commandLineArgs is null) + { + commandLineArgs = new Dictionary(); + var args = Environment.GetCommandLineArgs(); + for (var i = 1; i < args.Length; i += 2) + { + try + { + var key = args?[i]; + if (key != null) + { + if (!key.Contains("-")) + { + Logger.Write("Command line arguments are invalid."); + MessageBoxWrapper.Show("Command line arguments are invalid.", "Invalid Arguments", MessageBoxButton.OK, MessageBoxImage.Error); + i -= 1; + continue; + } + key = key.Trim().Replace("-", "").ToLower(); + if (i + 1 == args.Length) + { + commandLineArgs.Add(key, "true"); + continue; + } + var value = args?[i + 1]; + if (value != null) + { + if (value.StartsWith("-")) + { + commandLineArgs.Add(key, "true"); + i -= 1; + } + else + { + commandLineArgs.Add(key, args[i + 1].Trim()); + } + } + } + } + catch (Exception ex) + { + Logger.Write(ex); + } + + } + } + return commandLineArgs; + } + } + } +} diff --git a/Agent.Installer.Win/Services/InstallerService.cs b/Agent.Installer.Win/Services/InstallerService.cs index 283fb434..acee5d2b 100644 --- a/Agent.Installer.Win/Services/InstallerService.cs +++ b/Agent.Installer.Win/Services/InstallerService.cs @@ -1,38 +1,74 @@ -using System; +using Microsoft.Win32; +using Remotely.Shared.Models; +using System; using System.Collections.Generic; using System.Configuration.Install; using System.Diagnostics; using System.IO; +using System.IO.Compression; using System.Linq; +using System.Net; +using System.Reflection; using System.Security.Principal; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; +using System.Web.Script.Serialization; using System.Windows; namespace Remotely.Agent.Installer.Win.Services { public class InstallerService { - public bool Install() + public event EventHandler ProgressMessageChanged; + public event EventHandler ProgressValueChanged; + + private string InstallPath => Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Program Files", "Remotely"); + private string Platform => Environment.Is64BitOperatingSystem ? "x64" : "x86"; + private JavaScriptSerializer Serializer { get; } = new JavaScriptSerializer(); + public async Task Install(string serverUrl, + string organizationId, + string deviceGroup, + string deviceAlias) { try { + Logger.Write("Install started."); if (!CheckIsAdministrator()) { return false; } + await InstallDesktpRuntimeIfNeeded(); + + StopService(); + + BackupOrCreateDirectory(); + + var connectionInfo = GetConnectionInfo(organizationId, serverUrl); + + ClearInstallDirectory(); + + await DownloadRemotelyAgent(serverUrl); + + File.WriteAllText(Path.Combine(InstallPath, "ConnectionInfo.json"), Serializer.Serialize(connectionInfo)); + + File.Copy(Assembly.GetExecutingAssembly().Location, Path.Combine(InstallPath, "Remotely_Installer.exe")); + + CreateDeviceSetupOptions(deviceGroup, deviceAlias); + + AddFirewallRule(); + InstallService(); - var programPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Remotely", "ScreenCast", "Remotely_ScreenCast.exe"); - Process.Start("netsh", "advfirewall firewall delete rule name=\"Remotely ScreenCast\"").WaitForExit(); - Process.Start("netsh", $"advfirewall firewall add rule name=\"Remotely ScreenCast\" program=\"{programPath}\" protocol=any dir=in enable=yes action=allow profile=Private,Domain description=\"The agent that allows screen sharing and remote control for Remotely.\"").WaitForExit(); + CreateUninstallKey(); + return true; } catch (Exception ex) { Logger.Write(ex); + RestoreBackup(); return false; } @@ -47,8 +83,12 @@ namespace Remotely.Agent.Installer.Win.Services return false; } - Process.Start("cmd.exe", "/c sc delete Remotely_Service").WaitForExit(); + StopService(); + ProcessWrapper.StartHidden("cmd.exe", "/c sc delete Remotely_Service").WaitForExit(); + + + ProgressMessageChanged?.Invoke(this, "Stopping Remotely processes."); var procs = Process.GetProcessesByName("Remotely_Agent").Concat(Process.GetProcessesByName("Remotely_ScreenCast")); foreach (var proc in procs) @@ -58,9 +98,13 @@ namespace Remotely.Agent.Installer.Win.Services await Task.Delay(500); - Directory.Delete(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Remotely"), true); + ProgressMessageChanged?.Invoke(this, "Deleting files."); + ClearInstallDirectory(); + ProcessWrapper.StartHidden("cmd.exe", $"/c timeout 5 & rd /s /q \"{InstallPath}\""); - Process.Start("netsh", "advfirewall firewall delete rule name=\"Remotely ScreenCast\"").WaitForExit(); + ProcessWrapper.StartHidden("netsh", "advfirewall firewall delete rule name=\"Remotely ScreenCast\"").WaitForExit(); + + Registry.LocalMachine.DeleteSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Remotely", false); return true; @@ -72,6 +116,32 @@ namespace Remotely.Agent.Installer.Win.Services } } + private void AddFirewallRule() + { + var screenCastPath = Path.Combine(InstallPath, "ScreenCast", "Remotely_ScreenCast.exe"); + ProcessWrapper.StartHidden("netsh", "advfirewall firewall delete rule name=\"Remotely ScreenCast\"").WaitForExit(); + ProcessWrapper.StartHidden("netsh", $"advfirewall firewall add rule name=\"Remotely ScreenCast\" program=\"{screenCastPath}\" protocol=any dir=in enable=yes action=allow profile=Private,Domain description=\"The agent that allows screen sharing and remote control for Remotely.\"").WaitForExit(); + } + + private void BackupOrCreateDirectory() + { + if (Directory.Exists(InstallPath)) + { + Logger.Write("Backing up current installation."); + ProgressMessageChanged?.Invoke(this, "Backing up current installation."); + var backupPath = Path.Combine(Path.GetTempPath(), "Remotely_Backup.zip"); + if (File.Exists(backupPath)) + { + File.Delete(backupPath); + } + ZipFile.CreateFromDirectory(InstallPath, backupPath, CompressionLevel.Fastest, false); + } + else + { + Directory.CreateDirectory(InstallPath); + } + } + private bool CheckIsAdministrator() { var identity = WindowsIdentity.GetCurrent(); @@ -79,19 +149,159 @@ namespace Remotely.Agent.Installer.Win.Services var result = principal.IsInRole(WindowsBuiltInRole.Administrator); if (!result) { - MessageBox.Show("Elevated privileges are required. Please restart the installer using 'Run as administrator'.", "Elevation Required", MessageBoxButton.OK, MessageBoxImage.Warning); + MessageBoxWrapper.Show("Elevated privileges are required. Please restart the installer using 'Run as administrator'.", "Elevation Required", MessageBoxButton.OK, MessageBoxImage.Warning); } return result; } + private void ClearInstallDirectory() + { + if (Directory.Exists(InstallPath)) + { + foreach (var entry in Directory.GetFileSystemEntries(InstallPath)) + { + try + { + if (File.Exists(entry)) + { + File.Delete(entry); + } + else if (Directory.Exists(entry)) + { + Directory.Delete(entry, true); + } + } + catch (Exception ex) + { + Logger.Write(ex); + } + } + } + } + + private void CreateDeviceSetupOptions(string deviceGroup, string deviceAlias) + { + if (!string.IsNullOrWhiteSpace(deviceGroup) || + !string.IsNullOrWhiteSpace(deviceAlias)) + { + var setupOptions = new + { + DeviceGroup = deviceGroup, + DeviceAlias = deviceAlias + }; + + File.WriteAllText(Path.Combine(InstallPath, "DeviceSetupOptions.json"), Serializer.Serialize(setupOptions)); + } + } + + private void CreateUninstallKey() + { + var remotelyKey = Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Remotely", true); + remotelyKey.SetValue("DisplayIcon", Path.Combine(InstallPath, "Remotely_Agent.exe")); + remotelyKey.SetValue("DisplayName", "Remotely"); + remotelyKey.SetValue("DisplayVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString()); + remotelyKey.SetValue("InstallDate", DateTime.Now.ToShortDateString()); + remotelyKey.SetValue("Publisher", "Translucency Software"); + remotelyKey.SetValue("VersionMajor", Assembly.GetExecutingAssembly().GetName().Version.Major.ToString(), RegistryValueKind.DWord); + remotelyKey.SetValue("VersionMinor", Assembly.GetExecutingAssembly().GetName().Version.Minor.ToString(), RegistryValueKind.DWord); + remotelyKey.SetValue("UninstallString", Path.Combine(InstallPath, "Remotely_Installer.exe -uninstall")); + remotelyKey.SetValue("QuietUninstallString", Path.Combine(InstallPath, "Remotely_Installer.exe -uninstall -quiet")); + } + private async Task DownloadRemotelyAgent(string serverUrl) + { + ProgressMessageChanged.Invoke(this, "Downloading Remotely agent."); + var client = new WebClient(); + client.DownloadProgressChanged += (sender, args) => + { + ProgressValueChanged?.Invoke(this, args.ProgressPercentage); + }; + var targetFile = Path.Combine(Path.GetTempPath(), $"Remotely-Agent.zip"); + await client.DownloadFileTaskAsync($"{serverUrl}/Downloads/Remotely-Win10-{Platform}.zip", targetFile); + + ProgressMessageChanged.Invoke(this, "Extracting Remotely files."); + ProgressValueChanged?.Invoke(this, 0); + await Task.Run(() => { ZipFile.ExtractToDirectory(targetFile, InstallPath); }); + } + + private ConnectionInfo GetConnectionInfo(string organizationId, string serverUrl) + { + ConnectionInfo connectionInfo; + var connectionInfoPath = Path.Combine(InstallPath, "ConnectionInfo.json"); + if (File.Exists(connectionInfoPath)) + { + connectionInfo = Serializer.Deserialize(File.ReadAllText(connectionInfoPath)); + connectionInfo.ServerVerificationToken = null; + } + else + { + connectionInfo = new ConnectionInfo() + { + DeviceID = Guid.NewGuid().ToString() + }; + } + + connectionInfo.OrganizationID = organizationId; + connectionInfo.Host = serverUrl; + return connectionInfo; + } + + private async Task InstallDesktpRuntimeIfNeeded() + { + Logger.Write("Checking for .NET Core runtime."); + var uninstallKeys = new List(); + var runtimeInstalled = false; + + foreach (var subkeyName in Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\", false).GetSubKeyNames()) + { + var subkey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\" + subkeyName, false); + if (subkey?.GetValue("DisplayName")?.ToString()?.Contains("Microsoft Windows Desktop Runtime - 3.1.2") == true) + { + runtimeInstalled = true; + break; + } + } + + if (!runtimeInstalled) + { + Logger.Write("Downloading .NET Core runtime."); + ProgressMessageChanged.Invoke(this, "Downloading the .NET Core runtime."); + var client = new WebClient(); + client.DownloadProgressChanged += (sender, args) => + { + ProgressValueChanged?.Invoke(this, args.ProgressPercentage); + }; + var downloadUrl = string.Empty; + if (Environment.Is64BitOperatingSystem) + { + downloadUrl = "https://download.visualstudio.microsoft.com/download/pr/3240250e-6fe0-4258-af69-85abef6c00de/e01ee0af6c65d894f4a02bdf6705ec7b/windowsdesktop-runtime-3.1.2-win-x64.exe"; + } + else + { + downloadUrl = "https://download.visualstudio.microsoft.com/download/pr/b824906f-bd6e-4067-86a6-95c61620674d/cfcdab84a01cee94fdaa31271c3d4d47/windowsdesktop-runtime-3.1.2-win-x86.exe"; + } + var targetFile = Path.Combine(Path.GetTempPath(), "windowsdesktop-runtime.exe"); + await client.DownloadFileTaskAsync(downloadUrl, targetFile); + + Logger.Write("Installing .NET Core runtime."); + ProgressMessageChanged?.Invoke(this, "Installing the .NET Core runtime."); + ProgressValueChanged?.Invoke(this, 0); + + await Task.Run(() => { ProcessWrapper.StartHidden(targetFile, "/install /quiet /norestart").WaitForExit(); }); + } + else + { + Logger.Write(".NET Core runtime already installed."); + } + } + private void InstallService() { - Logger.Write("Install started."); - var installPath = System.Reflection.Assembly.GetExecutingAssembly().Location; + Logger.Write("Installing service."); + ProgressMessageChanged?.Invoke(this, "Installing Remotely service."); var serv = ServiceController.GetServices().FirstOrDefault(ser => ser.ServiceName == "Remotely_Service"); if (serv == null) { - var command = new string[] { "/assemblypath=" + installPath }; + var command = new string[] { "/assemblypath=" + Path.Combine(InstallPath, "Remotely_Agent.exe") }; var serviceInstaller = new ServiceInstaller(); var context = new InstallContext("", command); serviceInstaller.Context = context; @@ -104,16 +314,51 @@ namespace Remotely.Agent.Installer.Win.Services var state = new System.Collections.Specialized.ListDictionary(); serviceInstaller.Install(state); Logger.Write("Service installed."); + serv = ServiceController.GetServices().FirstOrDefault(ser => ser.ServiceName == "Remotely_Service"); + + ProcessWrapper.StartHidden("cmd.exe", "/c sc.exe failure \"Remotely_Service\" reset=5 actions=restart/5000"); } - serv = ServiceController.GetServices().FirstOrDefault(ser => ser.ServiceName == "Remotely_Service"); if (serv.Status != ServiceControllerStatus.Running) { serv.Start(); } Logger.Write("Service started."); - var psi = new ProcessStartInfo("cmd.exe", "/c sc.exe failure \"Remotely_Service\" reset=5 actions=restart/5000"); - psi.WindowStyle = ProcessWindowStyle.Hidden; - Process.Start(psi).WaitForExit(); + } + + private void RestoreBackup() + { + try + { + var backupPath = Path.Combine(Path.GetTempPath(), "Remotely_Backup.zip"); + if (File.Exists(backupPath)) + { + Logger.Write("Restoring backup."); + ClearInstallDirectory(); + ZipFile.ExtractToDirectory(backupPath, InstallPath); + } + } + catch (Exception ex) + { + Logger.Write(ex); + } + } + private void StopService() + { + try + { + var remotelyService = ServiceController.GetServices().FirstOrDefault(x => x.ServiceName == "Remotely_Service"); + if (remotelyService != null) + { + Logger.Write("Stopping existing Remotely service."); + ProgressMessageChanged?.Invoke(this, "Stopping existing Remotely service."); + remotelyService.Stop(); + remotelyService.WaitForStatus(ServiceControllerStatus.Stopped); + } + } + catch(Exception ex) + { + Logger.Write(ex); + } } } } diff --git a/Agent.Installer.Win/Services/MessageBoxWrapper.cs b/Agent.Installer.Win/Services/MessageBoxWrapper.cs new file mode 100644 index 00000000..4670a7a4 --- /dev/null +++ b/Agent.Installer.Win/Services/MessageBoxWrapper.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace Remotely.Agent.Installer.Win.Services +{ + public class MessageBoxWrapper + { + public static MessageBoxResult Show(string message, string caption, MessageBoxButton messageBoxButton, MessageBoxImage messageBoxImage) + { + if (!CommandLineParser.CommandLineArgs.ContainsKey("quiet")) + { + return MessageBox.Show(message, caption, messageBoxButton, messageBoxImage); + } + return MessageBoxResult.None; + } + } +} diff --git a/Agent.Installer.Win/Services/ProcessWrapper.cs b/Agent.Installer.Win/Services/ProcessWrapper.cs new file mode 100644 index 00000000..b00c3fcd --- /dev/null +++ b/Agent.Installer.Win/Services/ProcessWrapper.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Remotely.Agent.Installer.Win.Services +{ + public static class ProcessWrapper + { + public static Process StartHidden(string filePath, string arguments) + { + var psi = new ProcessStartInfo() + { + WindowStyle = ProcessWindowStyle.Hidden, + CreateNoWindow = true, + Arguments = arguments, + FileName = filePath + }; + return Process.Start(psi); + } + } +} diff --git a/Agent.Installer.Win/ViewModels/MainWindowViewModel.cs b/Agent.Installer.Win/ViewModels/MainWindowViewModel.cs index fe8ab098..964f50ae 100644 --- a/Agent.Installer.Win/ViewModels/MainWindowViewModel.cs +++ b/Agent.Installer.Win/ViewModels/MainWindowViewModel.cs @@ -1,16 +1,17 @@ using Remotely.Agent.Installer.Win.Services; +using Remotely.Shared.Models; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.Serialization.Json; using System.Security.Principal; using System.ServiceProcess; using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Web.Script.Serialization; using System.Windows; using System.Windows.Input; @@ -20,6 +21,7 @@ namespace Remotely.Agent.Installer.Win.ViewModels { private string headerMessage; + private bool isReadyState = true; private bool isServiceInstalled; private string organizationName; @@ -30,8 +32,6 @@ namespace Remotely.Agent.Installer.Win.ViewModels private string statusMessage; - private string subMessage; - public MainWindowViewModel() { Installer = new InstallerService(); @@ -49,10 +49,23 @@ namespace Remotely.Agent.Installer.Win.ViewModels } } - public ICommand InstallCommand => new Executor(Install); + public ICommand InstallCommand => new Executor(async (param) => { await Install(param); }); public bool IsProgressVisible => Progress > 0; + public bool IsReadyState + { + get + { + return isReadyState; + } + set + { + isReadyState = value; + FirePropertyChanged(nameof(IsReadyState)); + } + } + public bool IsServiceInstalled { get @@ -81,8 +94,8 @@ namespace Remotely.Agent.Installer.Win.ViewModels Process.Start(logPath); } else - { - MessageBox.Show("Log file doesn't exist.", "No Logs", MessageBoxButton.OK, MessageBoxImage.Information); + { + MessageBoxWrapper.Show("Log file doesn't exist.", "No Logs", MessageBoxButton.OK, MessageBoxImage.Information); } }); } @@ -141,33 +154,34 @@ namespace Remotely.Agent.Installer.Win.ViewModels } } - public string SubMessage - { - get - { - return subMessage; - } - set - { - subMessage = value; - FirePropertyChanged(nameof(SubMessage)); - } - } - public ICommand UninstallCommand => new Executor(async (param) => { await Uninstall(param); }); + + private string DeviceAlias { get; set; } + private string DeviceGroup { get; set; } private InstallerService Installer { get; } public async Task Init() { + + Installer.ProgressMessageChanged += (sender, arg) => + { + StatusMessage = arg; + }; + + Installer.ProgressValueChanged += (sender, arg) => + { + Progress = arg; + }; + IsServiceInstalled = ServiceController.GetServices().Any(x => x.ServiceName == "Remotely_Service"); - if (IsServiceInstalled) + if (IsServiceMissing) { HeaderMessage = "Install the Remotely service."; - SubMessage = "Installing the Remotely service will allow remote access by the above service provider."; + StatusMessage = "Installing the Remotely service will allow remote access by the above service provider."; } else { HeaderMessage = "Uninstall the Remotely service."; - SubMessage = "Uninstalling the Remotely service will remove all remote acess for the above service provider."; + StatusMessage = "Uninstalling the Remotely service will remove all remote acess for the above service provider."; } var installerSettings = ReadInstallerSettings(); @@ -176,10 +190,23 @@ namespace Remotely.Agent.Installer.Win.ViewModels ServerUrl = installerSettings?.ServerUrl; OrganizationID = installerSettings?.OrganizationID; - if (Environment.GetCommandLineArgs().Contains("-rununinstaller")) + CopyCommandLineArgs(); + + if (CommandLineParser.CommandLineArgs.ContainsKey("install")) + { + await Install(null); + } + else if (CommandLineParser.CommandLineArgs.ContainsKey("uninstall")) { await Uninstall(null); } + + if (CommandLineParser.CommandLineArgs.ContainsKey("quiet")) + { + App.Current.Shutdown(); + } + + CheckParams(); } public InstallerSettings ReadInstallerSettings() @@ -194,19 +221,16 @@ namespace Remotely.Agent.Installer.Win.ViewModels var payloadSize = br.ReadInt32(); peStream.Seek(-4 - payloadSize, SeekOrigin.End); var payloadBytes = br.ReadBytes(payloadSize); - using (var settingsStream = new MemoryStream(payloadBytes)) - { - settingsStream.Seek(0, SeekOrigin.Begin); - var serializer = new DataContractJsonSerializer(typeof(InstallerSettings)); - var installerSettings = (InstallerSettings)serializer.ReadObject(settingsStream); - return installerSettings; - } + var payloadJson = Encoding.UTF8.GetString(payloadBytes); + var serializer = new JavaScriptSerializer(); + var installerSettings = serializer.Deserialize(payloadJson); + return installerSettings; } } catch (Exception ex) { Logger.Write(ex); - MessageBox.Show("There was an error reading the installer settings. Try re-downloading the installer.", "Configuration Error", MessageBoxButton.OK, MessageBoxImage.Error); + MessageBoxWrapper.Show("There was an error reading the installer settings. Try re-downloading the installer.", "Configuration Error", MessageBoxButton.OK, MessageBoxImage.Error); return null; } } @@ -218,7 +242,7 @@ namespace Remotely.Agent.Installer.Win.ViewModels var result = principal.IsInRole(WindowsBuiltInRole.Administrator); if (!result) { - MessageBox.Show("Elevated privileges are required. Please restart the installer using 'Run as administrator'.", "Elevation Required", MessageBoxButton.OK, MessageBoxImage.Warning); + MessageBoxWrapper.Show("Elevated privileges are required. Please restart the installer using 'Run as administrator'.", "Elevation Required", MessageBoxButton.OK, MessageBoxImage.Warning); } return result; } @@ -228,33 +252,66 @@ namespace Remotely.Agent.Installer.Win.ViewModels var result = !string.IsNullOrWhiteSpace(OrganizationID) && !string.IsNullOrWhiteSpace(ServerUrl); if (!result) { - MessageBox.Show("Required settings are missing. Try re-downloading the installer.", "Invalid Installer", MessageBoxButton.OK, MessageBoxImage.Error); + MessageBoxWrapper.Show("Required settings are missing. Try re-downloading the installer.", "Invalid Installer", MessageBoxButton.OK, MessageBoxImage.Error); } return result; } - private void Install(object param) + private void CopyCommandLineArgs() + { + if (CommandLineParser.CommandLineArgs.TryGetValue("organization", out var orgName)) + { + OrganizationName = orgName; + } + + if (CommandLineParser.CommandLineArgs.TryGetValue("organizationid", out var orgID)) + { + OrganizationID = orgID; + } + + if (CommandLineParser.CommandLineArgs.TryGetValue("serverurl", out var serverUrl)) + { + ServerUrl = serverUrl; + } + + if (CommandLineParser.CommandLineArgs.TryGetValue("devicegroup", out var deviceGroup)) + { + DeviceGroup = deviceGroup; + } + + if (CommandLineParser.CommandLineArgs.TryGetValue("devicealias", out var deviceAlias)) + { + DeviceAlias = deviceAlias; + } + + if (ServerUrl?.EndsWith("/") == true) + { + ServerUrl = ServerUrl.Substring(0, ServerUrl.LastIndexOf("/")); + } + } + private async Task Install(object param) { try { + IsReadyState = false; if (!CheckParams()) { return; } - if (Installer.Install()) + if (await Installer.Install(ServerUrl, OrganizationID, DeviceGroup, DeviceAlias)) { IsServiceInstalled = true; Progress = 0; HeaderMessage = "Installation completed."; - SubMessage = "Remotely has been installed. You can now close this window."; + StatusMessage = "Remotely has been installed. You can now close this window."; } else { Progress = 0; HeaderMessage = "An error occurred during installation."; - SubMessage = "There was an error during installation. Check the logs for details."; + StatusMessage = "There was an error during installation. Check the logs for details."; } if (!CheckIsAdministrator()) { @@ -265,12 +322,20 @@ namespace Remotely.Agent.Installer.Win.ViewModels { Logger.Write(ex); } - + finally + { + IsReadyState = true; + } } + + + private async Task Uninstall(object param) { try { + IsReadyState = false; + if (!CheckParams()) { return; @@ -281,13 +346,13 @@ namespace Remotely.Agent.Installer.Win.ViewModels IsServiceInstalled = false; Progress = 0; HeaderMessage = "Uninstall completed."; - SubMessage = "Remotely has been uninstalled. You can now close this window."; + StatusMessage = "Remotely has been uninstalled. You can now close this window."; } else { Progress = 0; HeaderMessage = "An error occurred during uninstall."; - SubMessage = "There was an error during uninstall. Check the logs for details."; + StatusMessage = "There was an error during uninstall. Check the logs for details."; } } @@ -295,6 +360,10 @@ namespace Remotely.Agent.Installer.Win.ViewModels { Logger.Write(ex); } + finally + { + IsReadyState = true; + } } } } diff --git a/Agent/Agent.csproj b/Agent/Agent.csproj index e7920aa4..5acc5e7d 100644 --- a/Agent/Agent.csproj +++ b/Agent/Agent.csproj @@ -23,11 +23,11 @@ - - - + + + - + diff --git a/ScreenCast.Core/ScreenCast.Core.csproj b/ScreenCast.Core/ScreenCast.Core.csproj index ea44bfc4..04e3e0cb 100644 --- a/ScreenCast.Core/ScreenCast.Core.csproj +++ b/ScreenCast.Core/ScreenCast.Core.csproj @@ -32,10 +32,10 @@ - - - - + + + + diff --git a/ScreenCast.Win/ScreenCast.Win.csproj b/ScreenCast.Win/ScreenCast.Win.csproj index 2f1ca421..f1fba3be 100644 --- a/ScreenCast.Win/ScreenCast.Win.csproj +++ b/ScreenCast.Win/ScreenCast.Win.csproj @@ -23,8 +23,8 @@ - - + + diff --git a/Server/Server.csproj b/Server/Server.csproj index 4f0a7db4..fc46cdf7 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -63,20 +63,20 @@ - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - + diff --git a/Shared/Models/InstallerSettings.cs b/Shared/Models/InstallerSettings.cs index 95b6c76c..68b5ef9e 100644 --- a/Shared/Models/InstallerSettings.cs +++ b/Shared/Models/InstallerSettings.cs @@ -7,16 +7,12 @@ using System.Threading.Tasks; namespace Remotely.Shared.Models { - [DataContract] public class InstallerSettings { - [DataMember] public string OrganizationID { get; set; } - [DataMember] public string OrganizationName { get; set; } - [DataMember] public string ServerUrl { get; set; } } } diff --git a/Shared/Shared.csproj b/Shared/Shared.csproj index b2f8bc4d..f3c79058 100644 --- a/Shared/Shared.csproj +++ b/Shared/Shared.csproj @@ -8,10 +8,10 @@ - + - +