diff --git a/TwinpackShared/TwinpackUtils.cs b/TwinpackShared/TwinpackUtils.cs index 9899918..3a9e1fe 100644 --- a/TwinpackShared/TwinpackUtils.cs +++ b/TwinpackShared/TwinpackUtils.cs @@ -26,6 +26,7 @@ namespace Twinpack { public class TwinpackUtils { + private static readonly Guid _libraryManagerGuid = Guid.Parse("e1825adc-a79c-4e8e-8793-08d62d84be5b"); public static string DefaultLibraryCachePath { get { return $@"{Directory.GetCurrentDirectory()}\.Zeugwerk\libraries"; } } public static string LicensesPath = @"C:\TwinCAT\3.1\CustomConfig\Licenses"; @@ -74,6 +75,48 @@ public static Project ActivePlc(DTE2 dte) return null; } + public static void CloseAllPackageRelatedWindows(DTE2 dte, PackageVersionGetResponse packageVersion) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + // Close all windows that have been opened with a library manager + foreach (Window window in dte.Windows) + { + try + { + var obj = (window as dynamic).Object; + if (obj == null) + continue; + + if (obj.EditorViewFactoryGuid == _libraryManagerGuid) + { + window.Close(vsSaveChanges.vsSaveChangesNo); + continue; + } + + if (!(obj.FileName as string).StartsWith("[")) + continue; + + if (Regex.Match(obj.FileName, $@"\[{packageVersion.Name}.*?\({packageVersion.DistributorName}\)\].*", RegexOptions.IgnoreCase).Success) + { + window.Close(vsSaveChanges.vsSaveChangesNo); + continue; + } + + foreach (var dependency in packageVersion.Dependencies) + { + if (Regex.Match(obj.FileName, $@"\[{dependency.Name}.*?\({dependency.DistributorName}\)\].*", RegexOptions.IgnoreCase).Success) + { + window.Close(vsSaveChanges.vsSaveChangesNo); + break; + } + } + + } + catch { } + } + } + public static int BuildErrorCount(DTE2 dte) { int errorCount = 0; @@ -166,14 +209,14 @@ public static async Task> DownloadPackageVersion if (!referenceFound || forceDownload) { _logger.Info($"Downloading {packageVersion.Title} (version: {packageVersion.Version}, distributor: {packageVersion.DistributorName})"); - - downloadedPackageVersions.Add(await server.GetPackageVersionAsync((int)packageVersion.PackageVersionId, - includeBinary: true, cachePath: cachePath)); + var pk = await server.GetPackageVersionAsync((int)packageVersion.PackageVersionId, includeBinary: true, cachePath: cachePath); + downloadedPackageVersions.Add(pk); } foreach (var dependency in packageVersion?.Dependencies ?? new List()) { - downloadedPackageVersions.AddRange(await DownloadPackageVersionAndDependenciesAsync(libManager, dependency, server, forceDownload, cachePath)); + var pk = await DownloadPackageVersionAndDependenciesAsync(libManager, dependency, server, forceDownload, cachePath); + downloadedPackageVersions.AddRange(pk); } return downloadedPackageVersions; @@ -184,8 +227,7 @@ public static async Task InstallPackageVersionsAsync(ITcPlcLibraryManager libMan foreach(var packageVersion in packageVersions) { var suffix = packageVersion.Compiled == 1 ? "compiled-library" : "library"; - - await Task.Run( () => { libManager.InstallLibrary("System", $@"{cachePath ?? DefaultLibraryCachePath}\{packageVersion.Target}\{packageVersion.Name}_{packageVersion.Version}.{suffix}", bOverwrite: true); }); + await Task.Run(() => { libManager.InstallLibrary("System", $@"{cachePath ?? DefaultLibraryCachePath}\{packageVersion.Target}\{packageVersion.Name}_{packageVersion.Version}.{suffix}", bOverwrite: true); }); } } @@ -250,7 +292,6 @@ public static void RemoveReference(ITcPlcLibraryManager libManager, string place public static void AddReference(ITcPlcLibraryManager libManager, string placeholderName, string libraryName, string version, string distributorName, bool addAsPlaceholder = true) { - distributorName = distributorName ?? GuessDistributorName(libManager, libraryName, version); RemoveReference(libManager, placeholderName, libraryName, version, distributorName); diff --git a/TwinpackVsixShared/Dialogs/CatalogWindow.xaml b/TwinpackVsixShared/Dialogs/CatalogWindow.xaml index dde4019..6ddfcb8 100644 --- a/TwinpackVsixShared/Dialogs/CatalogWindow.xaml +++ b/TwinpackVsixShared/Dialogs/CatalogWindow.xaml @@ -279,6 +279,7 @@ diff --git a/TwinpackVsixShared/Dialogs/CatalogWindow.xaml.cs b/TwinpackVsixShared/Dialogs/CatalogWindow.xaml.cs index 2a9168a..66b6c9e 100644 --- a/TwinpackVsixShared/Dialogs/CatalogWindow.xaml.cs +++ b/TwinpackVsixShared/Dialogs/CatalogWindow.xaml.cs @@ -1,15 +1,15 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; -using System.Windows.Media; +using EnvDTE; using Microsoft.VisualStudio.Threading; using TCatSysManagerLib; @@ -29,6 +29,7 @@ public partial class CatalogWindow : UserControl, INotifyPropertyChanged private List _availablePackages = new List(); private List _installedPackages = new List(); private SemaphoreSlim _semaphorePackages = new SemaphoreSlim(1, 1); + private SemaphoreSlim _semaphoreAction = new SemaphoreSlim(1, 1); private List _packageVersions; private Models.CatalogItem _item = null; @@ -468,6 +469,7 @@ private async void Dialog_Loaded(object sender, RoutedEventArgs e) finally { IsCatalogLoading = false; + IsPackageVersionPanelEnabled = _plcConfig != null; IsUpdateAvailable = _twinpackServer.IsClientUpdateAvailable == true; btnLogin.Text = _twinpackServer.LoggedIn ? "Logout" : "Login"; btnRegister.Visibility = _twinpackServer.LoggedIn ? Visibility.Collapsed : Visibility.Visible; @@ -545,12 +547,17 @@ public async void EditPackageButton_Click(object sender, RoutedEventArgs e) _logger.Trace(ex); _logger.Error(ex.Message); } + finally + { + IsPackageVersionPanelEnabled = _plcConfig != null; + } } public async void UninstallPackageButton_Click(object sender, RoutedEventArgs e) { try { + _semaphoreAction.Wait(); IsPackageVersionPanelEnabled = false; await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); await _context?.Logger?.ActivateAsync(clear: true); @@ -583,6 +590,7 @@ public async void UninstallPackageButton_Click(object sender, RoutedEventArgs e) finally { IsPackageVersionPanelEnabled = true; + _semaphoreAction.Release(); } } @@ -590,6 +598,7 @@ public async void AddPackageButton_Click(object sender, RoutedEventArgs e) { try { + _semaphoreAction.Wait(); IsPackageVersionPanelEnabled = false; await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); await _context?.Logger?.ActivateAsync(clear: true); @@ -620,6 +629,7 @@ public async void AddPackageButton_Click(object sender, RoutedEventArgs e) finally { IsPackageVersionPanelEnabled = true; + _semaphoreAction.Release(); } } @@ -633,6 +643,7 @@ public async void RestoreAllPackageButton_Click(object sender, RoutedEventArgs e IEnumerable items = new List(); try { + _semaphoreAction.Wait(); IsRestoreAllEnabled = false; IsPackageVersionPanelEnabled = false; @@ -690,6 +701,7 @@ public async void RestoreAllPackageButton_Click(object sender, RoutedEventArgs e { IsPackageVersionPanelEnabled = true; IsRestoreAllEnabled = true; + _semaphoreAction.Release(); } } @@ -698,6 +710,8 @@ public async void UpdateAllPackageButton_Click(object sender, RoutedEventArgs e) IEnumerable items = new List(); try { + _semaphoreAction.Wait(); + IsPackageVersionPanelEnabled = false; IsUpdateAllEnabled = false; @@ -754,6 +768,7 @@ public async void UpdateAllPackageButton_Click(object sender, RoutedEventArgs e) { IsPackageVersionPanelEnabled = true; IsUpdateAllEnabled = true; + _semaphoreAction.Release(); } } @@ -831,8 +846,6 @@ public async Task UninstallPackageAsync() { await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - IsPackageVersionPanelEnabled = false; - if (PackageVersion.PackageVersionId == null) throw new Exception("No packages is selected that could be uninstalled!"); @@ -841,6 +854,7 @@ public async Task UninstallPackageAsync() var libManager = sysManager.LookupTreeItem(plc.PathName + "^References") as ITcPlcLibraryManager; _context.Dte.ExecuteCommand("File.SaveAll"); + TwinpackUtils.CloseAllPackageRelatedWindows(_context.Dte, PackageVersion); TwinpackUtils.RemoveReference(libManager, Package.Title, Package.Title, PackageVersion.Version, _package.DistributorName); _context.Dte.ExecuteCommand("File.SaveAll"); @@ -922,9 +936,13 @@ public async Task AddOrUpdatePackageAsync(Models.PackageVersionGetResponse packa } } - _logger.Info($"Installing package {packageVersion.Name} ..."); - var downloadPackageVersion = await TwinpackUtils.DownloadPackageVersionAndDependenciesAsync(libManager, packageVersion, _twinpackServer, forceDownload: ForcePackageVersionDownload, cachePath: cachePath); + + // Close Library Manager and all windows that are related to the library. These windows cause race conditions + TwinpackUtils.CloseAllPackageRelatedWindows(_context.Dte, packageVersion); + TwinpackUtils.RemoveReference(libManager, packageVersion.Title, packageVersion.Title, packageVersion.Version, packageVersion.DistributorName); + + _logger.Info($"Installing package {packageVersion.Name} ..."); await TwinpackUtils.InstallPackageVersionsAsync(libManager, downloadPackageVersion, cachePath: cachePath); await Task.Run(() => @@ -1021,7 +1039,7 @@ public void ShowLicenseTmcButton_Click(object sender, RoutedEventArgs e) } public void RegisterButton_Click(object sender, RoutedEventArgs e) { - Process.Start(_twinpackServer.RegisterUrl); + System.Diagnostics.Process.Start(_twinpackServer.RegisterUrl); } public async void LoginButton_Click(object sender, RoutedEventArgs e) @@ -1202,7 +1220,6 @@ private async Task LoadInstalledPackagesAsync() } IsNewReference = PackageVersion.PackageVersionId == null || !_installedPackages.Any(x => x.PackageId == PackageVersion.PackageId); - IsPackageVersionPanelEnabled = _plcConfig != null; InstalledPackagesCount = _installedPackages.Count(); UpdateablePackagesCount = _installedPackages.Where(x => x.IsUpdateable).Count(); } @@ -1393,6 +1410,7 @@ public async void ReloadButton_Click(object sender, RoutedEventArgs e) } finally { + IsPackageVersionPanelEnabled = _plcConfig != null; IsCatalogLoading = false; } } @@ -1422,7 +1440,7 @@ public async void CreateConfig_Click(object sender, RoutedEventArgs e) $"in {config.FilePath} for your TwinCAT solution, do you want to " + $"review and/or edit it?", "Configuration", MessageBoxButton.YesNo)) { - Process process = new Process + var process = new System.Diagnostics.Process { StartInfo = new ProcessStartInfo { @@ -1445,33 +1463,45 @@ public async void CreateConfig_Click(object sender, RoutedEventArgs e) _logger.Trace(ex); _logger.Error(ex.Message); } + finally + { + IsPackageVersionPanelEnabled = _plcConfig != null; + } } public void ShowProjectUrl_Click(object sender, RoutedEventArgs e) { - Process.Start(PackageVersion.ProjectUrl); + System.Diagnostics.Process.Start(PackageVersion.ProjectUrl); } public void UpdateAvailableButton_Click(object sender, RoutedEventArgs e) { - Process.Start(_twinpackServer?.UserInfo?.UpdateUrl); + System.Diagnostics.Process.Start(_twinpackServer?.UserInfo?.UpdateUrl); } public async void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e) { - var text = ((TextBox)sender).Text; - - if (IsBrowsingAvailablePackages) + try { - _searchText = text; - await Task.Delay(250); + var text = ((TextBox)sender).Text; + + if (IsBrowsingAvailablePackages) + { + _searchText = text; + await Task.Delay(250); - if(_searchText == text) - await LoadAvailablePackagesAsync(text); + if (_searchText == text) + await LoadAvailablePackagesAsync(text); + } + else + { + UpdateCatalog(); + } } - else + catch (Exception ex) { - UpdateCatalog(); + _logger.Trace(ex); + _logger.Error(ex.Message); } } }