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 @@
-
+
+
+
+
+
- Plugin enabled
+
+ Plugin enabled
+
+
_pluginManagementService.EnablePlugin(Plugin, true));