From e9fa1c3b5edf344dba4ee8509474e7e6629a3de5 Mon Sep 17 00:00:00 2001 From: Vest Date: Thu, 16 Dec 2021 12:30:12 +0100 Subject: [PATCH 1/7] Added Initial async support for AppUpdater Signed-off-by: Vest --- mRemoteNG/App/Startup.cs | 29 +--- mRemoteNG/App/Update/AppUpdater.cs | 151 ++++++++++++------ .../UI/Forms/OptionsPages/UpdatesPage.cs | 67 +++----- mRemoteNG/UI/Forms/frmMain.cs | 4 +- mRemoteNG/UI/Window/UpdateWindow.cs | 74 ++------- 5 files changed, 138 insertions(+), 187 deletions(-) diff --git a/mRemoteNG/App/Startup.cs b/mRemoteNG/App/Startup.cs index cb54bc55..ee6a6816 100644 --- a/mRemoteNG/App/Startup.cs +++ b/mRemoteNG/App/Startup.cs @@ -65,7 +65,7 @@ namespace mRemoteNG.App Runtime.ConnectionsService.RemoteConnectionsSyncronizer.Enable(); } - public void CheckForUpdate() + public async void CheckForUpdate() { if (_appUpdate == null) { @@ -87,32 +87,9 @@ namespace mRemoteNG.App return; } - _appUpdate.GetUpdateInfoCompletedEvent += GetUpdateInfoCompleted; - _appUpdate.GetUpdateInfoAsync(); - } - - private void GetUpdateInfoCompleted(object sender, AsyncCompletedEventArgs e) - { - if (_frmMain.InvokeRequired) - { - _frmMain.Invoke(new AsyncCompletedEventHandler(GetUpdateInfoCompleted), sender, e); - return; - } - try { - _appUpdate.GetUpdateInfoCompletedEvent -= GetUpdateInfoCompleted; - - if (e.Cancelled) - { - return; - } - - if (e.Error != null) - { - throw e.Error; - } - + await _appUpdate.GetUpdateInfoAsync(); if (_appUpdate.IsUpdateAvailable()) { Windows.Show(WindowType.Update); @@ -120,7 +97,7 @@ namespace mRemoteNG.App } catch (Exception ex) { - Runtime.MessageCollector.AddExceptionMessage("GetUpdateInfoCompleted() failed.", ex); + Runtime.MessageCollector.AddExceptionMessage("CheckForUpdate() failed.", ex); } } } diff --git a/mRemoteNG/App/Update/AppUpdater.cs b/mRemoteNG/App/Update/AppUpdater.cs index 5c21574c..e7c0e4bf 100644 --- a/mRemoteNG/App/Update/AppUpdater.cs +++ b/mRemoteNG/App/Update/AppUpdater.cs @@ -2,11 +2,13 @@ using System.IO; using System.Net; using System.ComponentModel; +using System.Net.Http; using System.Threading; using System.Reflection; using mRemoteNG.App.Info; using mRemoteNG.Security.SymmetricEncryption; using System.Security.Cryptography; +using System.Threading.Tasks; using mRemoteNG.Properties; #if !PORTABLE using mRemoteNG.Tools; @@ -22,29 +24,43 @@ namespace mRemoteNG.App.Update public class AppUpdater { private WebProxy _webProxy; - private Thread _getUpdateInfoThread; - private Thread _getChangeLogThread; + private HttpClient _httpClient; + // private Thread _getUpdateInfoThread; + // private Thread _getChangeLogThread; + private CancellationTokenSource changeLogCancelToken; + private CancellationTokenSource getUpdateInfoCancelToken; #region Public Properties public UpdateInfo CurrentUpdateInfo { get; private set; } + /* public string ChangeLog { get; private set; } + */ public bool IsGetUpdateInfoRunning { - get { return _getUpdateInfoThread != null && _getUpdateInfoThread.IsAlive; } + get + { + return getUpdateInfoCancelToken != null; + + } } private bool IsGetChangeLogRunning { - get { return _getChangeLogThread != null && _getChangeLogThread.IsAlive; } + get + { + return changeLogCancelToken != null; + } } + /* TODO: Review later public bool IsDownloadUpdateRunning { get { return _downloadUpdateWebClient != null; } } + */ #endregion @@ -52,10 +68,10 @@ namespace mRemoteNG.App.Update public AppUpdater() { - SetProxySettings(); + SetDefaultProxySettings(); } - private void SetProxySettings() + private void SetDefaultProxySettings() { var shouldWeUseProxy = Settings.Default.UpdateUseProxy; var proxyAddress = Settings.Default.UpdateProxyAddress; @@ -78,13 +94,14 @@ namespace mRemoteNG.App.Update if (useProxy && !string.IsNullOrEmpty(address)) { _webProxy = port != 0 ? new WebProxy(address, port) : new WebProxy(address); - _webProxy.Credentials = useAuthentication ? new NetworkCredential(username, password) : null; } else { _webProxy = null; } + + UpdateHttpClient(); } public bool IsUpdateAvailable() @@ -97,6 +114,7 @@ namespace mRemoteNG.App.Update return CurrentUpdateInfo.Version > GeneralAppInfo.GetApplicationVersion(); } + /* public void GetUpdateInfoAsync() { if (IsGetUpdateInfoRunning) @@ -109,7 +127,7 @@ namespace mRemoteNG.App.Update _getUpdateInfoThread.IsBackground = true; _getUpdateInfoThread.Start(); } - + public void GetChangeLogAsync() { if (CurrentUpdateInfo == null || !CurrentUpdateInfo.IsValid) @@ -128,10 +146,11 @@ namespace mRemoteNG.App.Update _getChangeLogThread.IsBackground = true; _getChangeLogThread.Start(); } + */ - public void DownloadUpdateAsync() + public async Task DownloadUpdateAsync() { - if (_downloadUpdateWebClient != null) + if (IsGetUpdateInfoRunning) { throw new InvalidOperationException("A previous call to DownloadUpdateAsync() is still in progress."); } @@ -160,17 +179,16 @@ namespace mRemoteNG.App.Update return; } #endif - DownloadUpdateWebClient.DownloadFileAsync(CurrentUpdateInfo.DownloadAddress, - CurrentUpdateInfo.UpdateFilePath); + // TODO: DownloadUpdateWebClient.DownloadFileAsync(CurrentUpdateInfo.DownloadAddress, CurrentUpdateInfo.UpdateFilePath); } #endregion #region Private Properties - private WebClient _downloadUpdateWebClient; - - private WebClient DownloadUpdateWebClient + /* + TODO: Review this part + private HttpClient DownloadUpdateWebClient { get { @@ -179,27 +197,38 @@ namespace mRemoteNG.App.Update return _downloadUpdateWebClient; } - _downloadUpdateWebClient = CreateWebClient(); + _downloadUpdateWebClient = CreateHttpClient(); - _downloadUpdateWebClient.DownloadProgressChanged += DownloadUpdateProgressChanged; - _downloadUpdateWebClient.DownloadFileCompleted += DownloadUpdateCompleted; + // TODO: _downloadUpdateWebClient.DownloadProgressChanged += DownloadUpdateProgressChanged; + // TODO: _downloadUpdateWebClient.DownloadFileCompleted += DownloadUpdateCompleted; return _downloadUpdateWebClient; } } + */ #endregion #region Private Methods - private WebClient CreateWebClient() + private void UpdateHttpClient() { - var webClient = new WebClient(); - webClient.Headers.Add("user-agent", GeneralAppInfo.UserAgent); - webClient.Proxy = _webProxy; - return webClient; + if (_httpClient != null) + { + _httpClient.Dispose(); + } + + var httpClientHandler = new HttpClientHandler(); + if (_webProxy != null) + { + httpClientHandler.UseProxy = true; + httpClientHandler.Proxy = _webProxy; + } + _httpClient = new HttpClient(httpClientHandler); + _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(GeneralAppInfo.UserAgent); } + /* TODO: Review this part private static DownloadStringCompletedEventArgs NewDownloadStringCompletedEventArgs(string result, Exception exception, bool cancelled, @@ -216,16 +245,16 @@ namespace mRemoteNG.App.Update return (DownloadStringCompletedEventArgs)constructor.Invoke(arguments); } + private DownloadStringCompletedEventArgs DownloadString(Uri address) { - var webClient = CreateWebClient(); var result = string.Empty; Exception exception = null; var cancelled = false; try { - result = webClient.DownloadString(address); + result = httpClient.GetStringAsync(address).Result; } catch (ThreadAbortException) { @@ -238,49 +267,65 @@ namespace mRemoteNG.App.Update return NewDownloadStringCompletedEventArgs(result, exception, cancelled, null); } + */ - private void GetUpdateInfo() + public async Task GetUpdateInfoAsync() { - var e = DownloadString(UpdateChannelInfo.GetUpdateChannelInfo()); - - if (!e.Cancelled && e.Error == null) + if (IsGetUpdateInfoRunning) { - try - { - CurrentUpdateInfo = UpdateInfo.FromString(e.Result); - - Settings.Default.CheckForUpdatesLastCheck = DateTime.UtcNow; - if (!Settings.Default.UpdatePending) - { - Settings.Default.UpdatePending = IsUpdateAvailable(); - } - } - catch (Exception ex) - { - e = NewDownloadStringCompletedEventArgs(e.Result, ex, e.Cancelled, null); - } + getUpdateInfoCancelToken.Cancel(); + getUpdateInfoCancelToken.Dispose(); + getUpdateInfoCancelToken = null; } - GetUpdateInfoCompletedEventEvent?.Invoke(this, e); + try + { + getUpdateInfoCancelToken = new CancellationTokenSource(); + var updateInfo = await _httpClient.GetStringAsync(UpdateChannelInfo.GetUpdateChannelInfo(), getUpdateInfoCancelToken.Token); + CurrentUpdateInfo = UpdateInfo.FromString(updateInfo); + Settings.Default.CheckForUpdatesLastCheck = DateTime.UtcNow; + + if (!Settings.Default.UpdatePending) + { + Settings.Default.UpdatePending = IsUpdateAvailable(); + } + } + finally + { + getUpdateInfoCancelToken.Dispose(); + getUpdateInfoCancelToken = null; + } } - private void GetChangeLog() + public async Task GetChangeLogAsync() { - var e = DownloadString(CurrentUpdateInfo.ChangeLogAddress); - - if (!e.Cancelled && e.Error == null) + if (IsGetChangeLogRunning) { - ChangeLog = e.Result; + changeLogCancelToken.Cancel(); + changeLogCancelToken.Dispose(); + changeLogCancelToken = null; } - GetChangeLogCompletedEventEvent?.Invoke(this, e); + try + { + changeLogCancelToken = new CancellationTokenSource(); + return await _httpClient.GetStringAsync(CurrentUpdateInfo.ChangeLogAddress, changeLogCancelToken.Token); + } + finally + { + changeLogCancelToken.Dispose(); + changeLogCancelToken = null; + } } + /* TODO private void DownloadUpdateProgressChanged(object sender, DownloadProgressChangedEventArgs e) { DownloadUpdateProgressChangedEventEvent?.Invoke(sender, e); } + */ + /* TODO private void DownloadUpdateCompleted(object sender, AsyncCompletedEventArgs e) { var raiseEventArgs = e; @@ -333,12 +378,12 @@ namespace mRemoteNG.App.Update _downloadUpdateWebClient.Dispose(); _downloadUpdateWebClient = null; - } + }*/ #endregion #region Events - + /* TODO: private AsyncCompletedEventHandler GetUpdateInfoCompletedEventEvent; public event AsyncCompletedEventHandler GetUpdateInfoCompletedEvent @@ -404,7 +449,7 @@ namespace mRemoteNG.App.Update (AsyncCompletedEventHandler)Delegate.Remove(DownloadUpdateCompletedEventEvent, value); } } - + */ #endregion } } \ No newline at end of file diff --git a/mRemoteNG/UI/Forms/OptionsPages/UpdatesPage.cs b/mRemoteNG/UI/Forms/OptionsPages/UpdatesPage.cs index 4e336e54..d269b31e 100644 --- a/mRemoteNG/UI/Forms/OptionsPages/UpdatesPage.cs +++ b/mRemoteNG/UI/Forms/OptionsPages/UpdatesPage.cs @@ -159,8 +159,6 @@ namespace mRemoteNG.UI.Forms.OptionsPages #endregion - #region Private Methods - #region Event Handlers private void chkCheckForUpdatesOnStartup_CheckedChanged(object sender, EventArgs e) @@ -194,7 +192,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages } } - private void btnTestProxy_Click(object sender, EventArgs e) + private async void btnTestProxy_Click(object sender, EventArgs e) { if (_appUpdate != null) { @@ -210,13 +208,27 @@ namespace mRemoteNG.UI.Forms.OptionsPages (int)numProxyPort.Value, chkUseProxyAuthentication.Checked, txtProxyUsername.Text, txtProxyPassword.Text); + try + { + btnTestProxy.Enabled = false; + btnTestProxy.Text = Language.OptionsProxyTesting; - btnTestProxy.Enabled = false; - btnTestProxy.Text = Language.OptionsProxyTesting; + await _appUpdate.GetUpdateInfoAsync(); - _appUpdate.GetUpdateInfoCompletedEvent += GetUpdateInfoCompleted; + btnTestProxy.Enabled = true; + btnTestProxy.Text = Language.TestProxy; + CTaskDialog.ShowCommandBox(this, Application.ProductName, Language.ProxyTestSucceeded, "", + Language._Ok, false); + } + catch (Exception ex) + { + CTaskDialog.ShowCommandBox(this, Application.ProductName, Language.ProxyTestFailed, + MiscTools.GetExceptionMessageRecursive(ex), "", "", "", Language._Ok, + false, + ESysIcons.Error, + 0); + } - _appUpdate.GetUpdateInfoAsync(); } private void chkUseProxyAuthentication_CheckedChanged(object sender, EventArgs e) @@ -228,46 +240,5 @@ namespace mRemoteNG.UI.Forms.OptionsPages } #endregion - - private void GetUpdateInfoCompleted(object sender, AsyncCompletedEventArgs e) - { - if (InvokeRequired) - { - AsyncCompletedEventHandler myDelegate = GetUpdateInfoCompleted; - Invoke(myDelegate, sender, e); - return; - } - - try - { - _appUpdate.GetUpdateInfoCompletedEvent -= GetUpdateInfoCompleted; - - btnTestProxy.Enabled = true; - btnTestProxy.Text = Language.TestProxy; - - if (e.Cancelled) - { - return; - } - - if (e.Error != null) - { - throw e.Error; - } - - CTaskDialog.ShowCommandBox(this, Application.ProductName, Language.ProxyTestSucceeded, "", - Language._Ok, false); - } - catch (Exception ex) - { - CTaskDialog.ShowCommandBox(this, Application.ProductName, Language.ProxyTestFailed, - MiscTools.GetExceptionMessageRecursive(ex), "", "", "", Language._Ok, - false, - ESysIcons.Error, - 0); - } - } - - #endregion } } \ No newline at end of file diff --git a/mRemoteNG/UI/Forms/frmMain.cs b/mRemoteNG/UI/Forms/frmMain.cs index a05b7d7e..05642c5a 100644 --- a/mRemoteNG/UI/Forms/frmMain.cs +++ b/mRemoteNG/UI/Forms/frmMain.cs @@ -339,7 +339,7 @@ namespace mRemoteNG.UI.Forms } } - private void frmMain_Shown(object sender, EventArgs e) + private async void frmMain_Shown(object sender, EventArgs e) { PromptForUpdatesPreference(); CheckForUpdates(); @@ -374,7 +374,7 @@ namespace mRemoteNG.UI.Forms } } - private void CheckForUpdates() + private async void CheckForUpdates() { if (!Settings.Default.CheckForUpdatesOnStartup) return; diff --git a/mRemoteNG/UI/Window/UpdateWindow.cs b/mRemoteNG/UI/Window/UpdateWindow.cs index 0d0da221..104cc011 100644 --- a/mRemoteNG/UI/Window/UpdateWindow.cs +++ b/mRemoteNG/UI/Window/UpdateWindow.cs @@ -38,7 +38,7 @@ namespace mRemoteNG.UI.Window #region Form Stuff - private void Update_Load(object sender, EventArgs e) + private async void Update_Load(object sender, EventArgs e) { ApplyTheme(); ThemeManager.getInstance().ThemeChanged += ApplyTheme; @@ -97,7 +97,7 @@ namespace mRemoteNG.UI.Window #region Private Methods - private void CheckForUpdate() + private async void CheckForUpdate() { if (_appUpdate == null) { @@ -119,39 +119,15 @@ namespace mRemoteNG.UI.Window SetVisibilityOfUpdateControls(false); - _appUpdate.GetUpdateInfoCompletedEvent += GetUpdateInfoCompleted; - - _appUpdate.GetUpdateInfoAsync(); - } - - private void GetUpdateInfoCompleted(object sender, AsyncCompletedEventArgs e) - { - if (InvokeRequired) - { - var myDelegate = new AsyncCompletedEventHandler(GetUpdateInfoCompleted); - Invoke(myDelegate, sender, e); - return; - } - try { - _appUpdate.GetUpdateInfoCompletedEvent -= GetUpdateInfoCompleted; + await _appUpdate.GetUpdateInfoAsync(); lblInstalledVersion.Text = Application.ProductVersion; lblInstalledVersion.Visible = true; lblInstalledVersionLabel.Visible = true; btnCheckForUpdate.Visible = true; - if (e.Cancelled) - { - return; - } - - if (e.Error != null) - { - throw e.Error; - } - if (_appUpdate.IsUpdateAvailable()) { lblStatus.Text = Language.UpdateAvailable; @@ -174,8 +150,15 @@ namespace mRemoteNG.UI.Window pbUpdateImage.Visible = true; } - _appUpdate.GetChangeLogCompletedEvent += GetChangeLogCompleted; - _appUpdate.GetChangeLogAsync(); + try + { + var changeLog = await _appUpdate.GetChangeLogAsync(); + txtChangeLog.Text = changeLog.Replace("\n", Environment.NewLine); + } + catch (Exception ex) + { + Runtime.MessageCollector?.AddExceptionStackTrace(Language.UpdateGetChangeLogFailed, ex); + } btnDownload.Focus(); } @@ -200,7 +183,7 @@ namespace mRemoteNG.UI.Window Runtime.MessageCollector?.AddExceptionStackTrace(Language.UpdateCheckCompleteFailed, ex); } } - + private void SetVisibilityOfUpdateControls(bool visible) { lblChangeLogLabel.Visible = visible; @@ -209,32 +192,6 @@ namespace mRemoteNG.UI.Window prgbDownload.Visible = visible; } - private void GetChangeLogCompleted(object sender, AsyncCompletedEventArgs e) - { - if (InvokeRequired) - { - var myDelegate = new AsyncCompletedEventHandler(GetChangeLogCompleted); - Invoke(myDelegate, sender, e); - return; - } - - try - { - _appUpdate.GetChangeLogCompletedEvent -= GetChangeLogCompleted; - - if (e.Cancelled) - return; - if (e.Error != null) - throw e.Error; - - txtChangeLog.Text = _appUpdate.ChangeLog.Replace("\n", Environment.NewLine); - } - catch (Exception ex) - { - Runtime.MessageCollector?.AddExceptionStackTrace(Language.UpdateGetChangeLogFailed, ex); - } - } - private void DownloadUpdate() { try @@ -245,8 +202,8 @@ namespace mRemoteNG.UI.Window if (_isUpdateDownloadHandlerDeclared == false) { - _appUpdate.DownloadUpdateProgressChangedEvent += DownloadUpdateProgressChanged; - _appUpdate.DownloadUpdateCompletedEvent += DownloadUpdateCompleted; + // _appUpdate.DownloadUpdateProgressChangedEvent += DownloadUpdateProgressChanged; + // _appUpdate.DownloadUpdateCompletedEvent += DownloadUpdateCompleted; _isUpdateDownloadHandlerDeclared = true; } @@ -262,6 +219,7 @@ namespace mRemoteNG.UI.Window #region Events + private void DownloadUpdateProgressChanged(object sender, DownloadProgressChangedEventArgs e) { prgbDownload.Value = e.ProgressPercentage; From 2fe85ae0e50592cc5bf42545efa635e52e780cd4 Mon Sep 17 00:00:00 2001 From: Vest Date: Thu, 16 Dec 2021 15:37:56 +0100 Subject: [PATCH 2/7] Added the progress handling; Now API (AppUpdater) is async. Cancellation token hasn't been tested yet, because UI doesn't allow to cancel the update. Signed-off-by: Vest --- mRemoteNG/App/Update/AppUpdater.cs | 46 +++++++++++++++++++++--- mRemoteNG/UI/Window/UpdateWindow.cs | 56 ++++++++--------------------- 2 files changed, 56 insertions(+), 46 deletions(-) diff --git a/mRemoteNG/App/Update/AppUpdater.cs b/mRemoteNG/App/Update/AppUpdater.cs index e7c0e4bf..05a0b5dc 100644 --- a/mRemoteNG/App/Update/AppUpdater.cs +++ b/mRemoteNG/App/Update/AppUpdater.cs @@ -148,10 +148,14 @@ namespace mRemoteNG.App.Update } */ - public async Task DownloadUpdateAsync() + public async Task DownloadUpdateAsync(IProgress progress) { if (IsGetUpdateInfoRunning) { + getUpdateInfoCancelToken.Cancel(); + getUpdateInfoCancelToken.Dispose(); + getUpdateInfoCancelToken = null; + throw new InvalidOperationException("A previous call to DownloadUpdateAsync() is still in progress."); } @@ -161,8 +165,7 @@ namespace mRemoteNG.App.Update "CurrentUpdateInfo is not valid. GetUpdateInfoAsync() must be called before calling DownloadUpdateAsync()."); } #if !PORTABLE - CurrentUpdateInfo.UpdateFilePath = - Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Path.GetRandomFileName(), "msi")); + CurrentUpdateInfo.UpdateFilePath = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Path.GetRandomFileName(), "msi")); #else var sfd = new SaveFileDialog { @@ -179,7 +182,42 @@ namespace mRemoteNG.App.Update return; } #endif - // TODO: DownloadUpdateWebClient.DownloadFileAsync(CurrentUpdateInfo.DownloadAddress, CurrentUpdateInfo.UpdateFilePath); + try + { + getUpdateInfoCancelToken = new CancellationTokenSource(); + using (var response = await _httpClient.GetAsync(CurrentUpdateInfo.DownloadAddress, + HttpCompletionOption.ResponseHeadersRead, getUpdateInfoCancelToken.Token)) + { + var buffer = new byte[8192]; + var totalBytes = response.Content.Headers.ContentLength ?? 0; + var readBytes = 0L; + + await using var httpStream = + await response.Content.ReadAsStreamAsync(getUpdateInfoCancelToken.Token); + await using var fileStream = new FileStream(CurrentUpdateInfo.UpdateFilePath, FileMode.Create, + FileAccess.Write, FileShare.None, buffer.Length, true); + + while (readBytes <= totalBytes || !getUpdateInfoCancelToken.IsCancellationRequested) + { + var bytesRead = await httpStream.ReadAsync(buffer, 0, buffer.Length, getUpdateInfoCancelToken.Token); + if (bytesRead == 0) + { + progress.Report(100); + break; + } + + await fileStream.WriteAsync(buffer, 0, bytesRead, getUpdateInfoCancelToken.Token); + + readBytes += bytesRead; + + var percentComplete = (int)(readBytes * 100 / totalBytes); + progress.Report(percentComplete); + } + } + } finally{ + getUpdateInfoCancelToken.Dispose(); + getUpdateInfoCancelToken = null; + } } #endregion diff --git a/mRemoteNG/UI/Window/UpdateWindow.cs b/mRemoteNG/UI/Window/UpdateWindow.cs index 104cc011..6adfeb2c 100644 --- a/mRemoteNG/UI/Window/UpdateWindow.cs +++ b/mRemoteNG/UI/Window/UpdateWindow.cs @@ -11,13 +11,14 @@ using mRemoteNG.Messages; using mRemoteNG.Themes; using WeifenLuo.WinFormsUI.Docking; using mRemoteNG.Resources.Language; +using System.Threading.Tasks; namespace mRemoteNG.UI.Window { public partial class UpdateWindow : BaseWindow { private AppUpdater _appUpdate; - private bool _isUpdateDownloadHandlerDeclared; + //private bool _isUpdateDownloadHandlerDeclared; #region Public Methods @@ -43,7 +44,7 @@ namespace mRemoteNG.UI.Window ApplyTheme(); ThemeManager.getInstance().ThemeChanged += ApplyTheme; ApplyLanguage(); - CheckForUpdate(); + CheckForUpdateAsync(); } private new void ApplyTheme() @@ -74,12 +75,12 @@ namespace mRemoteNG.UI.Window private void btnCheckForUpdate_Click(object sender, EventArgs e) { - CheckForUpdate(); + CheckForUpdateAsync(); } - private void btnDownload_Click(object sender, EventArgs e) + private async void btnDownload_Click(object sender, EventArgs e) { - DownloadUpdate(); + await DownloadUpdateAsync(); } private void pbUpdateImage_Click(object sender, EventArgs e) @@ -97,7 +98,7 @@ namespace mRemoteNG.UI.Window #region Private Methods - private async void CheckForUpdate() + private async void CheckForUpdateAsync() { if (_appUpdate == null) { @@ -192,7 +193,7 @@ namespace mRemoteNG.UI.Window prgbDownload.Visible = visible; } - private void DownloadUpdate() + private async Task DownloadUpdateAsync() { try { @@ -200,50 +201,18 @@ namespace mRemoteNG.UI.Window prgbDownload.Visible = true; prgbDownload.Value = 0; - if (_isUpdateDownloadHandlerDeclared == false) - { - // _appUpdate.DownloadUpdateProgressChangedEvent += DownloadUpdateProgressChanged; - // _appUpdate.DownloadUpdateCompletedEvent += DownloadUpdateCompleted; - _isUpdateDownloadHandlerDeclared = true; - } + await _appUpdate.DownloadUpdateAsync(new Progress(progress => prgbDownload.Value = progress)); - _appUpdate.DownloadUpdateAsync(); - } - catch (Exception ex) - { - Runtime.MessageCollector?.AddExceptionStackTrace(Language.UpdateDownloadFailed, ex); - } - } - - #endregion - - #region Events - - - private void DownloadUpdateProgressChanged(object sender, DownloadProgressChangedEventArgs e) - { - prgbDownload.Value = e.ProgressPercentage; - } - - private void DownloadUpdateCompleted(object sender, AsyncCompletedEventArgs e) - { - try - { btnDownload.Enabled = true; prgbDownload.Visible = false; - if (e.Cancelled) - return; - if (e.Error != null) - throw e.Error; - if (Runtime.IsPortableEdition) MessageBox.Show(Language.UpdatePortableDownloadComplete, Language.CheckForUpdates, - MessageBoxButtons.OK, MessageBoxIcon.Information); + MessageBoxButtons.OK, MessageBoxIcon.Information); else { if (MessageBox.Show(Language.UpdateDownloadComplete, Language.CheckForUpdates, - MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK) + MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK) { Shutdown.Quit(_appUpdate.CurrentUpdateInfo.UpdateFilePath); } @@ -255,8 +224,11 @@ namespace mRemoteNG.UI.Window } catch (Exception ex) { + Runtime.MessageCollector?.AddExceptionStackTrace(Language.UpdateDownloadFailed, ex); + Runtime.MessageCollector?.AddExceptionStackTrace(Language.UpdateDownloadCompleteFailed, ex); Runtime.MessageCollector?.AddMessage(MessageClass.ErrorMsg, ex.Message); + } } From 68dd6b1e230d8909c1a6afd6183d9f7fad12eb13 Mon Sep 17 00:00:00 2001 From: Vest Date: Thu, 16 Dec 2021 15:57:18 +0100 Subject: [PATCH 3/7] Removed unused code Added the checksum verification. Signed-off-by: Vest --- mRemoteNG/App/Update/AppUpdater.cs | 345 ++++++----------------------- 1 file changed, 62 insertions(+), 283 deletions(-) diff --git a/mRemoteNG/App/Update/AppUpdater.cs b/mRemoteNG/App/Update/AppUpdater.cs index 05a0b5dc..400f5620 100644 --- a/mRemoteNG/App/Update/AppUpdater.cs +++ b/mRemoteNG/App/Update/AppUpdater.cs @@ -23,26 +23,21 @@ namespace mRemoteNG.App.Update { public class AppUpdater { + private const int _bufferLength = 8192; private WebProxy _webProxy; private HttpClient _httpClient; - // private Thread _getUpdateInfoThread; - // private Thread _getChangeLogThread; - private CancellationTokenSource changeLogCancelToken; - private CancellationTokenSource getUpdateInfoCancelToken; + private CancellationTokenSource _changeLogCancelToken; + private CancellationTokenSource _getUpdateInfoCancelToken; #region Public Properties public UpdateInfo CurrentUpdateInfo { get; private set; } - /* - public string ChangeLog { get; private set; } - */ - public bool IsGetUpdateInfoRunning { get { - return getUpdateInfoCancelToken != null; + return _getUpdateInfoCancelToken != null; } } @@ -51,17 +46,10 @@ namespace mRemoteNG.App.Update { get { - return changeLogCancelToken != null; + return _changeLogCancelToken != null; } } - /* TODO: Review later - public bool IsDownloadUpdateRunning - { - get { return _downloadUpdateWebClient != null; } - } - */ - #endregion #region Public Methods @@ -113,56 +101,21 @@ namespace mRemoteNG.App.Update return CurrentUpdateInfo.Version > GeneralAppInfo.GetApplicationVersion(); } - - /* - public void GetUpdateInfoAsync() - { - if (IsGetUpdateInfoRunning) - { - _getUpdateInfoThread.Abort(); - } - - _getUpdateInfoThread = new Thread(GetUpdateInfo); - _getUpdateInfoThread.SetApartmentState(ApartmentState.STA); - _getUpdateInfoThread.IsBackground = true; - _getUpdateInfoThread.Start(); - } - public void GetChangeLogAsync() - { - if (CurrentUpdateInfo == null || !CurrentUpdateInfo.IsValid) - { - throw new InvalidOperationException( - "CurrentUpdateInfo is not valid. GetUpdateInfoAsync() must be called before calling GetChangeLogAsync()."); - } - - if (IsGetChangeLogRunning) - { - _getChangeLogThread.Abort(); - } - - _getChangeLogThread = new Thread(GetChangeLog); - _getChangeLogThread.SetApartmentState(ApartmentState.STA); - _getChangeLogThread.IsBackground = true; - _getChangeLogThread.Start(); - } - */ - public async Task DownloadUpdateAsync(IProgress progress) { if (IsGetUpdateInfoRunning) { - getUpdateInfoCancelToken.Cancel(); - getUpdateInfoCancelToken.Dispose(); - getUpdateInfoCancelToken = null; + _getUpdateInfoCancelToken.Cancel(); + _getUpdateInfoCancelToken.Dispose(); + _getUpdateInfoCancelToken = null; throw new InvalidOperationException("A previous call to DownloadUpdateAsync() is still in progress."); } if (CurrentUpdateInfo == null || !CurrentUpdateInfo.IsValid) { - throw new InvalidOperationException( - "CurrentUpdateInfo is not valid. GetUpdateInfoAsync() must be called before calling DownloadUpdateAsync()."); + throw new InvalidOperationException("CurrentUpdateInfo is not valid. GetUpdateInfoAsync() must be called before calling DownloadUpdateAsync()."); } #if !PORTABLE CurrentUpdateInfo.UpdateFilePath = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Path.GetRandomFileName(), "msi")); @@ -184,29 +137,28 @@ namespace mRemoteNG.App.Update #endif try { - getUpdateInfoCancelToken = new CancellationTokenSource(); - using (var response = await _httpClient.GetAsync(CurrentUpdateInfo.DownloadAddress, - HttpCompletionOption.ResponseHeadersRead, getUpdateInfoCancelToken.Token)) + _getUpdateInfoCancelToken = new CancellationTokenSource(); + using var response = await _httpClient.GetAsync(CurrentUpdateInfo.DownloadAddress, HttpCompletionOption.ResponseHeadersRead, _getUpdateInfoCancelToken.Token); + var buffer = new byte[_bufferLength]; + var totalBytes = response.Content.Headers.ContentLength ?? 0; + var readBytes = 0L; + + await using (var httpStream = await response.Content.ReadAsStreamAsync(_getUpdateInfoCancelToken.Token)) { - var buffer = new byte[8192]; - var totalBytes = response.Content.Headers.ContentLength ?? 0; - var readBytes = 0L; - - await using var httpStream = - await response.Content.ReadAsStreamAsync(getUpdateInfoCancelToken.Token); await using var fileStream = new FileStream(CurrentUpdateInfo.UpdateFilePath, FileMode.Create, - FileAccess.Write, FileShare.None, buffer.Length, true); + FileAccess.Write, FileShare.None, _bufferLength, true); - while (readBytes <= totalBytes || !getUpdateInfoCancelToken.IsCancellationRequested) + while (readBytes <= totalBytes || !_getUpdateInfoCancelToken.IsCancellationRequested) { - var bytesRead = await httpStream.ReadAsync(buffer, 0, buffer.Length, getUpdateInfoCancelToken.Token); + var bytesRead = + await httpStream.ReadAsync(buffer, 0, _bufferLength, _getUpdateInfoCancelToken.Token); if (bytesRead == 0) { progress.Report(100); break; } - await fileStream.WriteAsync(buffer, 0, bytesRead, getUpdateInfoCancelToken.Token); + await fileStream.WriteAsync(buffer, 0, bytesRead, _getUpdateInfoCancelToken.Token); readBytes += bytesRead; @@ -214,39 +166,39 @@ namespace mRemoteNG.App.Update progress.Report(percentComplete); } } + +#if !PORTABLE + var updateAuthenticode = new Authenticode(CurrentUpdateInfo.UpdateFilePath) + { + RequireThumbprintMatch = true, + ThumbprintToMatch = CurrentUpdateInfo.CertificateThumbprint + }; + + if (updateAuthenticode.Verify() != Authenticode.StatusValue.Verified) + { + if (updateAuthenticode.Status == Authenticode.StatusValue.UnhandledException) + { + throw updateAuthenticode.Exception; + } + + throw new Exception(updateAuthenticode.GetStatusMessage()); + } +#endif + + using var checksum = SHA512.Create(); + await using var stream = File.OpenRead(CurrentUpdateInfo.UpdateFilePath); + var hash = await checksum.ComputeHashAsync(stream); + var hashString = BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant(); + if (!hashString.Equals(CurrentUpdateInfo.Checksum)) + throw new Exception("SHA512 Hashes didn't match!"); } finally{ - getUpdateInfoCancelToken.Dispose(); - getUpdateInfoCancelToken = null; + _getUpdateInfoCancelToken?.Dispose(); + _getUpdateInfoCancelToken = null; } } #endregion - #region Private Properties - - /* - TODO: Review this part - private HttpClient DownloadUpdateWebClient - { - get - { - if (_downloadUpdateWebClient != null) - { - return _downloadUpdateWebClient; - } - - _downloadUpdateWebClient = CreateHttpClient(); - - // TODO: _downloadUpdateWebClient.DownloadProgressChanged += DownloadUpdateProgressChanged; - // TODO: _downloadUpdateWebClient.DownloadFileCompleted += DownloadUpdateCompleted; - - return _downloadUpdateWebClient; - } - } - */ - - #endregion - #region Private Methods private void UpdateHttpClient() @@ -266,60 +218,19 @@ namespace mRemoteNG.App.Update _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(GeneralAppInfo.UserAgent); } - /* TODO: Review this part - private static DownloadStringCompletedEventArgs NewDownloadStringCompletedEventArgs(string result, - Exception exception, - bool cancelled, - object userToken) - { - var type = typeof(DownloadStringCompletedEventArgs); - const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance; - Type[] argumentTypes = {typeof(string), typeof(Exception), typeof(bool), typeof(object)}; - var constructor = type.GetConstructor(bindingFlags, null, argumentTypes, null); - object[] arguments = {result, exception, cancelled, userToken}; - - if (constructor == null) - return null; - return (DownloadStringCompletedEventArgs)constructor.Invoke(arguments); - } - - - private DownloadStringCompletedEventArgs DownloadString(Uri address) - { - var result = string.Empty; - Exception exception = null; - var cancelled = false; - - try - { - result = httpClient.GetStringAsync(address).Result; - } - catch (ThreadAbortException) - { - cancelled = true; - } - catch (Exception ex) - { - exception = ex; - } - - return NewDownloadStringCompletedEventArgs(result, exception, cancelled, null); - } - */ - public async Task GetUpdateInfoAsync() { if (IsGetUpdateInfoRunning) { - getUpdateInfoCancelToken.Cancel(); - getUpdateInfoCancelToken.Dispose(); - getUpdateInfoCancelToken = null; + _getUpdateInfoCancelToken.Cancel(); + _getUpdateInfoCancelToken.Dispose(); + _getUpdateInfoCancelToken = null; } try { - getUpdateInfoCancelToken = new CancellationTokenSource(); - var updateInfo = await _httpClient.GetStringAsync(UpdateChannelInfo.GetUpdateChannelInfo(), getUpdateInfoCancelToken.Token); + _getUpdateInfoCancelToken = new CancellationTokenSource(); + var updateInfo = await _httpClient.GetStringAsync(UpdateChannelInfo.GetUpdateChannelInfo(), _getUpdateInfoCancelToken.Token); CurrentUpdateInfo = UpdateInfo.FromString(updateInfo); Settings.Default.CheckForUpdatesLastCheck = DateTime.UtcNow; @@ -330,8 +241,8 @@ namespace mRemoteNG.App.Update } finally { - getUpdateInfoCancelToken.Dispose(); - getUpdateInfoCancelToken = null; + _getUpdateInfoCancelToken?.Dispose(); + _getUpdateInfoCancelToken = null; } } @@ -339,155 +250,23 @@ namespace mRemoteNG.App.Update { if (IsGetChangeLogRunning) { - changeLogCancelToken.Cancel(); - changeLogCancelToken.Dispose(); - changeLogCancelToken = null; + _changeLogCancelToken.Cancel(); + _changeLogCancelToken.Dispose(); + _changeLogCancelToken = null; } try { - changeLogCancelToken = new CancellationTokenSource(); - return await _httpClient.GetStringAsync(CurrentUpdateInfo.ChangeLogAddress, changeLogCancelToken.Token); + _changeLogCancelToken = new CancellationTokenSource(); + return await _httpClient.GetStringAsync(CurrentUpdateInfo.ChangeLogAddress, _changeLogCancelToken.Token); } finally { - changeLogCancelToken.Dispose(); - changeLogCancelToken = null; + _changeLogCancelToken?.Dispose(); + _changeLogCancelToken = null; } } - /* TODO - private void DownloadUpdateProgressChanged(object sender, DownloadProgressChangedEventArgs e) - { - DownloadUpdateProgressChangedEventEvent?.Invoke(sender, e); - } - */ - - /* TODO - private void DownloadUpdateCompleted(object sender, AsyncCompletedEventArgs e) - { - var raiseEventArgs = e; - - if (!e.Cancelled && e.Error == null) - { - try - { -#if !PORTABLE - var updateAuthenticode = new Authenticode(CurrentUpdateInfo.UpdateFilePath) - { - RequireThumbprintMatch = true, - ThumbprintToMatch = CurrentUpdateInfo.CertificateThumbprint - }; - - if (updateAuthenticode.Verify() != Authenticode.StatusValue.Verified) - { - if (updateAuthenticode.Status == Authenticode.StatusValue.UnhandledException) - { - throw updateAuthenticode.Exception; - } - - throw new Exception(updateAuthenticode.GetStatusMessage()); - } -#endif - - using (var cksum = SHA512.Create()) - { - using (var stream = File.OpenRead(CurrentUpdateInfo.UpdateFilePath)) - { - var hash = cksum.ComputeHash(stream); - var hashString = BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant(); - if (!hashString.Equals(CurrentUpdateInfo.Checksum)) - throw new Exception("SHA512 Hashes didn't match!"); - } - } - } - catch (Exception ex) - { - raiseEventArgs = new AsyncCompletedEventArgs(ex, false, null); - } - } - - if (raiseEventArgs.Cancelled || raiseEventArgs.Error != null) - { - File.Delete(CurrentUpdateInfo.UpdateFilePath); - } - - DownloadUpdateCompletedEventEvent?.Invoke(this, raiseEventArgs); - - _downloadUpdateWebClient.Dispose(); - _downloadUpdateWebClient = null; - }*/ - - #endregion - - #region Events - /* TODO: - private AsyncCompletedEventHandler GetUpdateInfoCompletedEventEvent; - - public event AsyncCompletedEventHandler GetUpdateInfoCompletedEvent - { - add - { - GetUpdateInfoCompletedEventEvent = - (AsyncCompletedEventHandler)Delegate.Combine(GetUpdateInfoCompletedEventEvent, value); - } - remove - { - GetUpdateInfoCompletedEventEvent = - (AsyncCompletedEventHandler)Delegate.Remove(GetUpdateInfoCompletedEventEvent, value); - } - } - - private AsyncCompletedEventHandler GetChangeLogCompletedEventEvent; - - public event AsyncCompletedEventHandler GetChangeLogCompletedEvent - { - add - { - GetChangeLogCompletedEventEvent = - (AsyncCompletedEventHandler)Delegate.Combine(GetChangeLogCompletedEventEvent, value); - } - remove - { - GetChangeLogCompletedEventEvent = - (AsyncCompletedEventHandler)Delegate.Remove(GetChangeLogCompletedEventEvent, value); - } - } - - private DownloadProgressChangedEventHandler DownloadUpdateProgressChangedEventEvent; - - public event DownloadProgressChangedEventHandler DownloadUpdateProgressChangedEvent - { - add - { - DownloadUpdateProgressChangedEventEvent = - (DownloadProgressChangedEventHandler)Delegate.Combine(DownloadUpdateProgressChangedEventEvent, - value); - } - remove - { - DownloadUpdateProgressChangedEventEvent = - (DownloadProgressChangedEventHandler)Delegate.Remove(DownloadUpdateProgressChangedEventEvent, - value); - } - } - - private AsyncCompletedEventHandler DownloadUpdateCompletedEventEvent; - - public event AsyncCompletedEventHandler DownloadUpdateCompletedEvent - { - add - { - DownloadUpdateCompletedEventEvent = - (AsyncCompletedEventHandler)Delegate.Combine(DownloadUpdateCompletedEventEvent, value); - } - remove - { - DownloadUpdateCompletedEventEvent = - (AsyncCompletedEventHandler)Delegate.Remove(DownloadUpdateCompletedEventEvent, value); - } - } - */ #endregion } } \ No newline at end of file From 2bdfd7401e3b1ad206d3b0a561fa0861aefeb09f Mon Sep 17 00:00:00 2001 From: Vest Date: Thu, 16 Dec 2021 16:41:00 +0100 Subject: [PATCH 4/7] Corrected warnings related to async event handlers Signed-off-by: Vest --- mRemoteNG/App/Startup.cs | 3 ++- mRemoteNG/App/Update/AppUpdater.cs | 1 - mRemoteNG/UI/Forms/frmMain.cs | 7 ++++--- mRemoteNG/UI/Window/UpdateWindow.cs | 8 ++++---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/mRemoteNG/App/Startup.cs b/mRemoteNG/App/Startup.cs index ee6a6816..e7a13bce 100644 --- a/mRemoteNG/App/Startup.cs +++ b/mRemoteNG/App/Startup.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using System.Diagnostics; using System.Globalization; +using System.Threading.Tasks; using mRemoteNG.App.Info; using mRemoteNG.App.Initialization; using mRemoteNG.App.Update; @@ -65,7 +66,7 @@ namespace mRemoteNG.App Runtime.ConnectionsService.RemoteConnectionsSyncronizer.Enable(); } - public async void CheckForUpdate() + public async Task CheckForUpdate() { if (_appUpdate == null) { diff --git a/mRemoteNG/App/Update/AppUpdater.cs b/mRemoteNG/App/Update/AppUpdater.cs index 400f5620..a878e28c 100644 --- a/mRemoteNG/App/Update/AppUpdater.cs +++ b/mRemoteNG/App/Update/AppUpdater.cs @@ -38,7 +38,6 @@ namespace mRemoteNG.App.Update get { return _getUpdateInfoCancelToken != null; - } } diff --git a/mRemoteNG/UI/Forms/frmMain.cs b/mRemoteNG/UI/Forms/frmMain.cs index 05642c5a..d8c25c3e 100644 --- a/mRemoteNG/UI/Forms/frmMain.cs +++ b/mRemoteNG/UI/Forms/frmMain.cs @@ -25,6 +25,7 @@ using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Text; +using System.Threading.Tasks; using System.Windows.Forms; using mRemoteNG.UI.Panels; using WeifenLuo.WinFormsUI.Docking; @@ -342,7 +343,7 @@ namespace mRemoteNG.UI.Forms private async void frmMain_Shown(object sender, EventArgs e) { PromptForUpdatesPreference(); - CheckForUpdates(); + await CheckForUpdates(); } private void PromptForUpdatesPreference() @@ -374,7 +375,7 @@ namespace mRemoteNG.UI.Forms } } - private async void CheckForUpdates() + private async Task CheckForUpdates() { if (!Settings.Default.CheckForUpdatesOnStartup) return; @@ -385,7 +386,7 @@ namespace mRemoteNG.UI.Forms if (!IsHandleCreated) CreateHandle(); // Make sure the handle is created so that InvokeRequired returns the correct result - Startup.Instance.CheckForUpdate(); + await Startup.Instance.CheckForUpdate(); } private void frmMain_FormClosing(object sender, FormClosingEventArgs e) diff --git a/mRemoteNG/UI/Window/UpdateWindow.cs b/mRemoteNG/UI/Window/UpdateWindow.cs index 6adfeb2c..8553b3a0 100644 --- a/mRemoteNG/UI/Window/UpdateWindow.cs +++ b/mRemoteNG/UI/Window/UpdateWindow.cs @@ -44,7 +44,7 @@ namespace mRemoteNG.UI.Window ApplyTheme(); ThemeManager.getInstance().ThemeChanged += ApplyTheme; ApplyLanguage(); - CheckForUpdateAsync(); + await CheckForUpdateAsync(); } private new void ApplyTheme() @@ -73,9 +73,9 @@ namespace mRemoteNG.UI.Window lblLatestVersionLabel.Text = $"{Language.AvailableVersion}:"; } - private void btnCheckForUpdate_Click(object sender, EventArgs e) + private async void btnCheckForUpdate_Click(object sender, EventArgs e) { - CheckForUpdateAsync(); + await CheckForUpdateAsync(); } private async void btnDownload_Click(object sender, EventArgs e) @@ -98,7 +98,7 @@ namespace mRemoteNG.UI.Window #region Private Methods - private async void CheckForUpdateAsync() + private async Task CheckForUpdateAsync() { if (_appUpdate == null) { From 0e8d573819c1b17fb8e461b66fa5ed613dfde42e Mon Sep 17 00:00:00 2001 From: Vest Date: Thu, 16 Dec 2021 17:00:49 +0100 Subject: [PATCH 5/7] Updated changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07988a62..5a3fb4a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] ### Added +- #2086: Replace WebClient with async HttpClient for updater. - #1850: Minify config xml - #1770: Added missing RDP performance settings - #1516: added API to access credential vault (Thycotic Secret Server) by specifying SSAPI:ID as username @@ -38,7 +39,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - #1720: Show configuration file name in title of password prompt form - #1713: Sound redirection does not work if Clipboard redirection is set to No - #1632: 1.77.1 breaks RDP drive and sound redirection -- #1610: Menu bar changes to english when cancelling options form +- #1610: Menu bar changes to english when canceling options form - #1595: Unhandled exception when trying to browse through non existent multi ssh history with keyboard key strokes - #1589: Update SQL tables instead of rewriting them - #1465: REGRESSION: Smart Cards redirection to Remote Desktop not working From 47e37afe124b993031348b51898315c5b6cfe1a5 Mon Sep 17 00:00:00 2001 From: Vest Date: Thu, 16 Dec 2021 17:03:56 +0100 Subject: [PATCH 6/7] Removed unused imports for AppUpdater.cs Signed-off-by: Vest --- mRemoteNG/App/Update/AppUpdater.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/mRemoteNG/App/Update/AppUpdater.cs b/mRemoteNG/App/Update/AppUpdater.cs index a878e28c..4bfc6b06 100644 --- a/mRemoteNG/App/Update/AppUpdater.cs +++ b/mRemoteNG/App/Update/AppUpdater.cs @@ -1,10 +1,8 @@ using System; using System.IO; using System.Net; -using System.ComponentModel; using System.Net.Http; using System.Threading; -using System.Reflection; using mRemoteNG.App.Info; using mRemoteNG.Security.SymmetricEncryption; using System.Security.Cryptography; From 8bac5a8c54eb39de66931fc68c895b3198e69b70 Mon Sep 17 00:00:00 2001 From: Vest Date: Thu, 16 Dec 2021 17:05:05 +0100 Subject: [PATCH 7/7] Removed unused imports for UpdateWindow.cs --- mRemoteNG/UI/Window/UpdateWindow.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/mRemoteNG/UI/Window/UpdateWindow.cs b/mRemoteNG/UI/Window/UpdateWindow.cs index 8553b3a0..e7d405c9 100644 --- a/mRemoteNG/UI/Window/UpdateWindow.cs +++ b/mRemoteNG/UI/Window/UpdateWindow.cs @@ -1,9 +1,7 @@ using System; -using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; -using System.Net; using System.Windows.Forms; using mRemoteNG.App; using mRemoteNG.App.Update;