diff --git a/src/Artemis.Core/Plugins/Plugin.cs b/src/Artemis.Core/Plugins/Plugin.cs index 0575a80ac..53c581e7c 100644 --- a/src/Artemis.Core/Plugins/Plugin.cs +++ b/src/Artemis.Core/Plugins/Plugin.cs @@ -178,12 +178,15 @@ namespace Artemis.Core { foreach (PluginFeature feature in Features) feature.Dispose(); + SetEnabled(false); Kernel?.Dispose(); PluginLoader?.Dispose(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + _features.Clear(); - SetEnabled(false); } } diff --git a/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs b/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs index 83bab9211..887321a72 100644 --- a/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs +++ b/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs @@ -70,6 +70,12 @@ namespace Artemis.Core.Services /// The resulting plugin Plugin ImportPlugin(string fileName); + /// + /// Unloads and permanently removes the provided plugin + /// + /// The plugin to remove + void RemovePlugin(Plugin plugin); + /// /// Enables the provided plugin feature /// diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs index 31dc0070c..66b7933ec 100644 --- a/src/Artemis.Core/Services/PluginManagementService.cs +++ b/src/Artemis.Core/Services/PluginManagementService.cs @@ -5,6 +5,7 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; +using System.Runtime.Loader; using Artemis.Core.DeviceProviders; using Artemis.Core.Ninject; using Artemis.Storage.Entities.Plugins; @@ -275,6 +276,7 @@ namespace Artemis.Core.Services plugin.PluginLoader = PluginLoader.CreateFromAssemblyFile(mainFile!, configure => { configure.IsUnloadable = true; + configure.LoadInMemory = true; configure.PreferSharedTypes = true; }); @@ -430,7 +432,6 @@ namespace Artemis.Core.Services OnPluginDisabled(new PluginEventArgs(plugin)); } - /// public Plugin ImportPlugin(string fileName) { DirectoryInfo pluginDirectory = new(Path.Combine(Constants.DataFolder, "plugins")); @@ -449,7 +450,16 @@ namespace Artemis.Core.Services Plugin? existing = _plugins.FirstOrDefault(p => p.Guid == pluginInfo.Guid); if (existing != null) - throw new ArtemisPluginException($"A plugin with the same GUID is already loaded: {existing.Info}"); + { + try + { + RemovePlugin(existing); + } + catch (Exception e) + { + throw new ArtemisPluginException("A plugin with the same GUID is already loaded, failed to remove old version", e); + } + } string targetDirectory = pluginInfo.Main.Split(".dll")[0].Replace("/", "").Replace("\\", ""); string uniqueTargetDirectory = targetDirectory; @@ -464,19 +474,38 @@ namespace Artemis.Core.Services // Extract everything in the same archive directory to the unique plugin directory DirectoryInfo directoryInfo = new(Path.Combine(pluginDirectory.FullName, uniqueTargetDirectory)); - Directory.CreateDirectory(directoryInfo.FullName); + Utilities.CreateAccessibleDirectory(directoryInfo.FullName); string metaDataDirectory = metaDataFileEntry.FullName.Replace(metaDataFileEntry.Name, ""); foreach (ZipArchiveEntry zipArchiveEntry in archive.Entries) + { if (zipArchiveEntry.FullName.StartsWith(metaDataDirectory)) { string target = Path.Combine(directoryInfo.FullName, zipArchiveEntry.FullName.Remove(0, metaDataDirectory.Length)); - zipArchiveEntry.ExtractToFile(target); + // Create folders + if (zipArchiveEntry.FullName.EndsWith("/")) + Utilities.CreateAccessibleDirectory(Path.GetDirectoryName(target)!); + // Extract files + else + zipArchiveEntry.ExtractToFile(target); } + } // Load the newly extracted plugin and return the result return LoadPlugin(directoryInfo); } + public void RemovePlugin(Plugin plugin) + { + DirectoryInfo directory = plugin.Directory; + lock (_plugins) + { + if (_plugins.Contains(plugin)) + UnloadPlugin(plugin); + } + + directory.Delete(true); + } + #endregion #region Features diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs index 5d17a2c72..787ab9c83 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs @@ -59,12 +59,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins { Items.Clear(); await Task.Delay(200); - _instances = _pluginManagementService.GetAllPlugins() - .Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p)) - .OrderBy(i => i.Plugin.Info.Name) - .ToList(); - - UpdatePluginSearch(); + GetPluginInstances(); }); base.OnActivate(); @@ -80,14 +75,21 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins { Plugin plugin = _pluginManagementService.ImportPlugin(dialog.FileName); - _instances = _pluginManagementService.GetAllPlugins() - .Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p)) - .OrderBy(i => i.Plugin.Info.Name) - .ToList(); + GetPluginInstances(); SearchPluginInput = plugin.Info.Name; - + _messageService.ShowMessage($"Imported plugin: {plugin.Info.Name}"); } } + + public void GetPluginInstances() + { + _instances = _pluginManagementService.GetAllPlugins() + .Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p)) + .OrderBy(i => i.Plugin.Info.Name) + .ToList(); + + UpdatePluginSearch(); + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsView.xaml b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsView.xaml index 250c2682b..34f106e9f 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsView.xaml +++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsView.xaml @@ -53,15 +53,28 @@ - + SETTINGS + + + + - Plugin enabled + + Plugin enabled + + _pluginManagementService.EnablePlugin(Plugin, true));