Installer app completed.

This commit is contained in:
Jared Goodwin 2020-02-22 13:28:35 -08:00 committed by Jared Goodwin
parent b41659a10e
commit 30b2c5afaa
17 changed files with 551 additions and 139 deletions

View File

@ -50,10 +50,12 @@
<Reference Include="System" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Data" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Runtime.Serialization">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.2\System.Runtime.Serialization.dll</HintPath>
</Reference>
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
@ -72,10 +74,18 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Models\InstallerSettings.cs" />
<Compile Include="..\Shared\Models\ConnectionInfo.cs">
<Link>Models\ConnectionInfo.cs</Link>
</Compile>
<Compile Include="..\Shared\Models\InstallerSettings.cs">
<Link>Models\InstallerSettings.cs</Link>
</Compile>
<Compile Include="Services\CommandLineParser.cs" />
<Compile Include="Services\MessageBoxWrapper.cs" />
<Compile Include="Services\Executor.cs" />
<Compile Include="Services\InstallerService.cs" />
<Compile Include="Services\Logger.cs" />
<Compile Include="Services\ProcessWrapper.cs" />
<Compile Include="ViewModels\MainWindowViewModel.cs" />
<Compile Include="ViewModels\ViewModelBase.cs" />
<Page Include="MainWindow.xaml">

View File

@ -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">
<Application.Resources>
<Style x:Key="TitlebarButton" TargetType="Button">
<Setter Property="Background" Value="#FF464646"></Setter>
@ -25,6 +25,7 @@
<Setter Property="BorderThickness" Value="1"></Setter>
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="Padding" Value="6,4"></Setter>
<Setter Property="FontSize" Value="14"></Setter>
</Style>
</Application.Resources>
</Application>

View File

@ -17,24 +17,6 @@ namespace Remotely.Agent.Installer.Win
/// </summary>
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);
}
}
}
}
}

View File

@ -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">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
@ -36,17 +38,25 @@
<StackPanel>
<TextBlock Style="{StaticResource SectionHeader}" Text="{Binding HeaderMessage}"></TextBlock>
<TextBlock TextWrapping="Wrap" Margin="25,5,0,0">
<Run>Provider: </Run>
<Run Text="{Binding OrganizationName}"></Run>
</TextBlock>
<TextBlock TextWrapping="Wrap" Margin="25,5,0,0">
<Run>Server URL: </Run>
<Run Text="{Binding ServerUrl}"></Run>
</TextBlock>
<TextBlock TextWrapping="Wrap" Margin="0,15,0,0" Text="{Binding SubMessage}"></TextBlock>
<ProgressBar Height="25" Margin="0,25,0,0" Value="{Binding Progress}" Visibility="{Binding IsProgressVisible, Converter={StaticResource BooleanToVisibilityConverter}}"></ProgressBar>
<TextBlock TextWrapping="Wrap" Margin="0,15,0,0" Text="{Binding StatusMessage}"></TextBlock>
<Grid Margin="25,5,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="Provider:" Margin="0,0,15,0" FontSize="14" />
<TextBox Grid.Column="1" Grid.Row="0" IsReadOnly="True" Margin="0,0,0,10" TextWrapping="Wrap" Text="{Binding OrganizationName}" FontSize="14" />
<TextBlock Grid.Column="0" Grid.Row="1" Margin="0,0,15,0" Text="Server URL:" FontSize="14" />
<TextBox Grid.Column="1" Grid.Row="1" IsReadOnly="True" TextWrapping="Wrap" Text="{Binding ServerUrl}" FontSize="14" />
</Grid>
<TextBlock TextWrapping="Wrap" Margin="0,25,0,0" Text="{Binding StatusMessage}" FontSize="14"></TextBlock>
<ProgressBar Height="25" Margin="0,10,0,0" Value="{Binding Progress}" Visibility="{Binding IsProgressVisible, Converter={StaticResource BooleanToVisibilityConverter}}"></ProgressBar>
</StackPanel>
</Grid>
@ -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

View File

@ -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)

View File

@ -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; }
}
}

View File

@ -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<string, string> commandLineArgs;
public static Dictionary<string, string> CommandLineArgs
{
get
{
if (commandLineArgs is null)
{
commandLineArgs = new Dictionary<string, string>();
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;
}
}
}
}

View File

@ -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<string> ProgressMessageChanged;
public event EventHandler<int> 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<bool> 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<ConnectionInfo>(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<RegistryKey>();
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);
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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<InstallerSettings>(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;
}
}
}
}

View File

@ -23,11 +23,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="3.1.2" />
<PackageReference Include="Microsoft.Management.Infrastructure" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.2" />
<PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="6.2.4" />
<PackageReference Include="Microsoft.PowerShell.Commands.Management" Version="6.2.4" />
<PackageReference Include="Microsoft.PowerShell.Commands.Utility" Version="6.2.4" />

View File

@ -32,10 +32,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.2" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="3.1.2" />
<PackageReference Include="Microsoft.MixedReality.WebRTC" Version="1.0.2" />
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
</ItemGroup>

View File

@ -23,8 +23,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.MixedReality.WebRTC" Version="1.0.2" />
<PackageReference Include="NAudio" Version="1.10.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.2" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.2" />
<PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
<PackageReference Include="SharpDX.DXGI" Version="4.2.0" />

View File

@ -63,20 +63,20 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.1">
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.1.2" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="3.1.2" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.1" PrivateAssets="All" />
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.1.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0" />
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
</ItemGroup>

View File

@ -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; }
}
}

View File

@ -8,10 +8,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.2" />
<PackageReference Include="Microsoft.Management.Infrastructure" Version="2.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
<PackageReference Include="System.Text.Json" Version="4.7.0" />
<PackageReference Include="System.Text.Json" Version="4.7.1" />
</ItemGroup>
<ItemGroup>