1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Plugins have their own AppDomain now (TODO: dispose on plugin unload)

This commit is contained in:
SpoinkyNL 2019-04-13 12:13:44 +02:00
parent 3689db9325
commit dba48a2b6a
18 changed files with 118 additions and 69 deletions

View File

@ -51,6 +51,9 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="AppDomainToolkit, Version=1.0.4.1, Culture=neutral, PublicKeyToken=f2fc7ab5180cf5c4, processorArchitecture=MSIL">
<HintPath>..\packages\AppDomainToolkit.1.0.4.3\lib\net\AppDomainToolkit.dll</HintPath>
</Reference>
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll</HintPath>
</Reference>

View File

@ -18,7 +18,7 @@ namespace Artemis.Core.Plugins.Abstract
public abstract bool ExpandsMainDataModel { get; }
/// <inheritdoc />
public void LoadPlugin()
public void EnablePlugin()
{
// Load and activate the last active profile
}

View File

@ -10,6 +10,6 @@ namespace Artemis.Core.Plugins.Interfaces
/// <summary>
/// Called when the plugin is loaded
/// </summary>
void LoadPlugin();
void EnablePlugin();
}
}

View File

@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using AppDomainToolkit;
using Artemis.Core.Plugins.Interfaces;
using Newtonsoft.Json;
@ -7,11 +7,6 @@ namespace Artemis.Core.Plugins.Models
{
public class PluginInfo
{
public PluginInfo()
{
Instances = new List<IPlugin>();
}
/// <summary>
/// The plugins GUID
/// </summary>
@ -39,10 +34,21 @@ namespace Artemis.Core.Plugins.Models
public string Folder { get; set; }
/// <summary>
/// A references to the types implementing IPlugin, available after successful load
/// A reference to the type implementing IPlugin, available after successful load
/// </summary>
[JsonIgnore]
public List<IPlugin> Instances { get; set; }
public IPlugin Instance { get; set; }
/// <summary>
/// Indicates whether the user enabled the plugin or not
/// </summary>
[JsonIgnore]
public bool Enabled { get; set; }
/// <summary>
/// The AppDomain context of this plugin
/// </summary>
internal AppDomainContext<AssemblyTargetLoader, PathBasedAssemblyResolver> Context { get; set; }
public override string ToString()
{

View File

@ -48,7 +48,7 @@ namespace Artemis.Core.Services
{
try
{
var modules = _pluginService.Plugins.SelectMany(p => p.Instances).OfType<IModule>().ToList();
var modules = _pluginService.GetModules();
// Update all active modules
foreach (var module in modules)

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Artemis.Core.Events;
@ -45,5 +46,11 @@ namespace Artemis.Core.Services.Interfaces
/// <param name="layerTypeGuid">The GUID of the layer type to find</param>
/// <returns>An instance of the layer type</returns>
ILayerType GetLayerTypeByGuid(Guid layerTypeGuid);
/// <summary>
/// Returns all the plugins implementing <see cref="IModule" />
/// </summary>
/// <returns></returns>
IReadOnlyList<IModule> GetModules();
}
}

View File

@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using AppDomainToolkit;
using Artemis.Core.Events;
using Artemis.Core.Exceptions;
using Artemis.Core.Plugins.Exceptions;
@ -53,7 +53,6 @@ namespace Artemis.Core.Services
// Load the plugin assemblies into the plugin context
var directory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins"));
foreach (var subDirectory in directory.EnumerateDirectories())
{
try
{
// Load the metadata
@ -63,42 +62,58 @@ namespace Artemis.Core.Services
// Locate the main entry
var pluginInfo = JsonConvert.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile));
// TODO Just temporarily until settings are in place
pluginInfo.Enabled = true;
var mainFile = Path.Combine(subDirectory.FullName, pluginInfo.Main);
if (!File.Exists(mainFile))
throw new ArtemisPluginException(pluginInfo, "Couldn't find the plugins main entry at " + mainFile);
// Load the plugin, all types implementing IPlugin and register them with DI
Assembly assembly;
var setupInfo = new AppDomainSetup
{
ApplicationName = pluginInfo.Guid.ToString(),
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
PrivateBinPath = subDirectory.FullName
};
pluginInfo.Context = AppDomainContext.Create(setupInfo);
try
{
assembly = Assembly.LoadFile(mainFile);
pluginInfo.Context.LoadAssemblyWithReferences(LoadMethod.LoadFrom, mainFile);
}
catch (Exception e)
{
throw new ArtemisPluginException(pluginInfo, "Failed to load the plugins assembly", e);
}
var pluginTypes = assembly.GetTypes().Where(t => typeof(IPlugin).IsAssignableFrom(t)).ToArray();
foreach (var pluginType in pluginTypes)
{
_childKernel.Bind<IPlugin>().To(pluginType).InSingletonScope();
try
{
pluginInfo.Instances.Add((IPlugin) _childKernel.Get(pluginType));
}
catch (Exception e)
{
throw new ArtemisPluginException(pluginInfo, "Failed to instantiate the plugin", e);
}
}
// Get the IPlugin implementation from the main assembly and if there is only one, instantiate it
var mainAssembly = pluginInfo.Context.Domain.GetAssemblies().First(a => a.Location == mainFile);
var pluginTypes = mainAssembly.GetTypes().Where(t => typeof(IPlugin).IsAssignableFrom(t)).ToList();
if (pluginTypes.Count > 1)
throw new ArtemisPluginException(pluginInfo, $"Plugin contains {pluginTypes.Count} implementations of IPlugin, only 1 allowed");
if (pluginTypes.Count == 0)
throw new ArtemisPluginException(pluginInfo, "Plugin contains no implementation of IPlugin");
var pluginType = pluginTypes.Single();
_childKernel.Bind<IPlugin>().To(pluginType).InSingletonScope();
try
{
pluginInfo.Instance = (IPlugin) _childKernel.Get(pluginType);
}
catch (Exception e)
{
throw new ArtemisPluginException(pluginInfo, "Failed to instantiate the plugin", e);
}
_plugins.Add(pluginInfo);
}
catch (Exception e)
{
throw new ArtemisPluginException("Failed to load plugin", e);
}
}
// Activate plugins after they are all loaded
foreach (var pluginInfo in _plugins.Where(p => p.Enabled))
pluginInfo.Instance.EnablePlugin();
});
OnFinishedLoadedPlugins();
@ -111,11 +126,15 @@ namespace Artemis.Core.Services
if (pluginInfo == null)
return null;
var layerType = pluginInfo.Instances.SingleOrDefault(p => p is ILayerType);
if (layerType == null)
if (!(pluginInfo.Instance is ILayerType layerType))
throw new ArtemisPluginException(pluginInfo, "Plugin is expected to implement exactly one ILayerType");
return (ILayerType) layerType;
return layerType;
}
public IReadOnlyList<IModule> GetModules()
{
return Plugins.Where(p => p.Instance is IModule).Select(p => (IModule) p.Instance).ToList();
}
public void Dispose()

View File

@ -28,8 +28,7 @@
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
@ -53,8 +52,7 @@
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Scripting" publicKeyToken="31bf3856ad364e35"
culture="neutral" />
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Scripting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
@ -70,8 +68,7 @@
<bindingRedirect oldVersion="0.0.0.0-2.6.0.0" newVersion="2.6.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0" />
</dependentAssembly>
<dependentAssembly>
@ -87,8 +84,7 @@
<bindingRedirect oldVersion="0.0.0.0-2.2.0.0" newVersion="2.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" />
</dependentAssembly>
<dependentAssembly>
@ -96,8 +92,7 @@
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51"
culture="neutral" />
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.1" newVersion="4.0.3.1" />
</dependentAssembly>
<dependentAssembly>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AppDomainToolkit" version="1.0.4.3" targetFramework="net461" />
<package id="Castle.Core" version="4.4.0" targetFramework="net461" />
<package id="Newtonsoft.Json" version="12.0.1" targetFramework="net461" />
<package id="Ninject" version="3.3.4" targetFramework="net461" />

View File

@ -33,17 +33,23 @@
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="QRCoder, Version=1.2.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\QRCoder.1.2.5\lib\net40\QRCoder.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Core, Version=0.1.22.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Core.0.1.22\lib\net45\RGB.NET.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Stylet, Version=1.1.22.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Stylet.1.1.22\lib\net45\Stylet.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
@ -52,6 +58,9 @@
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="UnityEngine, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\QRCoder.1.2.5\lib\net40\UnityEngine.dll</HintPath>
</Reference>
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
@ -63,6 +72,7 @@
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj">
<Project>{9b811f9b-86b9-4771-87af-72bae7078a36}</Project>
<Name>Artemis.Core</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>

View File

@ -1,7 +1,7 @@
using System;
using System.Drawing;
using System.Drawing;
using Artemis.Core.Plugins.Interfaces;
using Artemis.Core.ProfileElements;
using QRCoder;
using RGB.NET.Core;
namespace Artemis.Plugins.LayerTypes.Brush
@ -10,12 +10,11 @@ namespace Artemis.Plugins.LayerTypes.Brush
{
public void Dispose()
{
throw new NotImplementedException();
}
public void LoadPlugin()
public void EnablePlugin()
{
throw new NotImplementedException();
var qrGenerator = new QRCodeGenerator();
}
public void Update(Layer layer)

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="QRCoder" version="1.2.5" targetFramework="net461" />
<package id="RGB.NET.Core" version="0.1.22" targetFramework="net461" />
<package id="Stylet" version="1.1.22" targetFramework="net461" />
<package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
</packages>

View File

@ -33,17 +33,27 @@
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="QRCoder, Version=1.3.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\QRCoder.1.3.5\lib\net40\QRCoder.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Core, Version=0.1.22.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RGB.NET.Core.0.1.22\lib\net45\RGB.NET.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Stylet, Version=1.1.22.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Stylet.1.1.22\lib\net45\Stylet.dll</HintPath>
<Reference Include="Stylet, Version=1.1.17.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Stylet.1.1.17\lib\net45\Stylet.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Drawing.Common, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Drawing.Common.4.5.0\lib\net461\System.Drawing.Common.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
@ -69,6 +79,7 @@
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj">
<Project>{9b811f9b-86b9-4771-87af-72bae7078a36}</Project>
<Name>Artemis.Core</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>

View File

@ -6,11 +6,11 @@ namespace Artemis.Plugins.Modules.General
{
public class GeneralDataModel : ModuleDataModel
{
[DataModelProperty(DisplayName = "Unique boolean")]
public bool PropertyUniqueToThisDm { get; set; }
public GeneralDataModel(IModule module) : base(module)
{
}
[DataModelProperty(DisplayName = "Unique boolean")]
public bool PropertyUniqueToThisDm { get; set; }
}
}

View File

@ -4,6 +4,7 @@ using Artemis.Core;
using Artemis.Core.Plugins.Interfaces;
using Artemis.Core.Services.Interfaces;
using Artemis.Plugins.Modules.General.ViewModels;
using QRCoder;
using RGB.NET.Core;
using Stylet;
using Color = System.Drawing.Color;
@ -63,8 +64,9 @@ namespace Artemis.Plugins.Modules.General
_colors = null;
}
public void LoadPlugin()
public void EnablePlugin()
{
var qrGenerator = new QRCodeGenerator();
PopulateColors();
}

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="QRCoder" version="1.3.5" targetFramework="net461" />
<package id="RGB.NET.Core" version="0.1.22" targetFramework="net461" />
<package id="Stylet" version="1.1.22" targetFramework="net461" />
<package id="Stylet" version="1.1.17" targetFramework="net461" />
<package id="System.Drawing.Common" version="4.5.0" targetFramework="net461" />
<package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
</packages>

View File

@ -31,8 +31,7 @@
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
@ -56,8 +55,7 @@
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Scripting" publicKeyToken="31bf3856ad364e35"
culture="neutral" />
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Scripting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
@ -73,8 +71,7 @@
<bindingRedirect oldVersion="0.0.0.0-2.6.0.0" newVersion="2.6.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0" />
</dependentAssembly>
<dependentAssembly>
@ -90,8 +87,7 @@
<bindingRedirect oldVersion="0.0.0.0-2.2.0.0" newVersion="2.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" />
</dependentAssembly>
<dependentAssembly>
@ -99,8 +95,7 @@
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51"
culture="neutral" />
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.1" newVersion="4.0.3.1" />
</dependentAssembly>
<dependentAssembly>

View File

@ -34,7 +34,7 @@ namespace Artemis.UI.ViewModels
_pluginService.FinishedLoadedPlugins += PluginServiceOnFinishedLoadedPlugins;
if (!LoadingPlugins)
Modules.AddRange(_pluginService.Plugins.SelectMany(p => p.Instances.Where(i => i is IModule).Cast<IModule>()));
Modules.AddRange(_pluginService.GetModules());
PropertyChanged += OnSelectedModuleChanged;
PropertyChanged += OnSelectedPageChanged;
@ -58,7 +58,7 @@ namespace Artemis.UI.ViewModels
private void PluginServiceOnFinishedLoadedPlugins(object sender, EventArgs eventArgs)
{
Modules.AddRange(_pluginService.Plugins.SelectMany(p => p.Instances.Where(i => i is IModule).Cast<IModule>()));
Modules.AddRange(_pluginService.GetModules());
SelectedModule = null;
LoadingPlugins = false;