In my project I need to use plugins. But to use these in my project I need to import an reference of the plugin. Since I can't know how many or which plugins the project uses beforehand I would like to import them dynamically in my project.
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = new IPlugin[pluginFiles.Length];
Assembly asm;
for (int i = 0; i < pluginFiles.Length; i++)
{
string args = pluginFiles[i].Substring(
pluginFiles[i].LastIndexOf("\\") + 1,
pluginFiles[i].IndexOf(".dll") -
pluginFiles[i].LastIndexOf("\\") - 1);
asm = Assembly.LoadFile(pluginFiles[i]);
Type[] types = asm.GetTypes();
In this code example I searched all the .dll files and put them into a string list.
But how can I now load all these .dll files? Or is there a way to use these .dll files without really importing them?
The MEF (Managed Extensibility Framework) Method:
You'll want to add references to System.ComponentModel.Composition to your projects that utilize the import/export functionality of MEF.
First, the bootstrapper/loader (in my case, I just added it to the Main class).
Program.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using MEFContract;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
var prgm = new Program();
// Search the "Plugins" subdirectory for assemblies that match the imports.
var catalog = new DirectoryCatalog("Plugins");
using (var container = new CompositionContainer(catalog))
{
// Match Imports in "prgm" object with corresponding exports in all catalogs in the container
container.ComposeParts(prgm);
}
prgm.DoStuff();
Console.Read();
}
private void DoStuff()
{
foreach (var plugin in Plugins)
plugin.DoPluginStuff();
}
[ImportMany] // This is a signal to the MEF framework to load all matching exported assemblies.
private IEnumerable<IPlugin> Plugins { get; set; }
}
}
The IPlugin interface is the contract between the imports & exports. All plugins will implement this interface. The contract is pretty simple:
IPlugin.cs:
namespace MEFContract
{
public interface IPlugin
{
void DoPluginStuff();
}
}
Finally, you can create as many plugins as you like in different assemblies. They must implement the contract interface and also be decorated with the "Export" attribute to indicate to MEF that they should be matched up with any corresponding imports. Then drop the dlls in a "Plugins" folder (this folder should reside in the same location as the executable). Here's a sample plugin:
Plugin.cs:
using System;
using System.ComponentModel.Composition;
using MEFContract;
namespace Plugin
{
[Export(typeof(IPlugin))]
public class Plugin : IPlugin
{
public void DoPluginStuff()
{
Console.WriteLine("Doing my thing!");
}
}
}
Let's assume for the sake of simplicity that all of the implementations of IPlugin have default constructors (public and no parameters).
That said, you really want to find all types that implement this interface and create an instance of them. You're on the right track somewhat, but you can simplify this tremendously with a little LINQ:
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = (
// From each file in the files.
from file in pluginFiles
// Load the assembly.
let asm = Assembly.LoadFile(file)
// For every type in the assembly that is visible outside of
// the assembly.
from type in asm.GetExportedTypes()
// Where the type implements the interface.
where typeof(IPlugin).IsAssignableFrom(type)
// Create the instance.
select (IPlugin) Activator.CreateInstance(type)
// Materialize to an array.
).ToArray();
That said, you might be better off using a dependency injection framework; they usually allow for dynamic loading and binding to interface implementations in assemblies not referenced at compile time.
Also, while a bit convoluted (in my opinion), you might want to look at the System.AddIn namespaces, as they are built specifically for this purpose. However, the dependency injection route is usually much easier if you don't have to worry about version control of contracts and the like.
I have an application which can not only load plugin at runtime, but also hot-load and unload them as user drop them in the folder, take them out or erase them. So, when I need to recompile my plugin, I don't need to re-launch my application. In my case, all plugin derive from the Plugin abstract, so they are easy to find in .DLL.
Here's my loading method:
private static void LoadPlugins(FileInfo file)
{
try
{
Assembly assembly = Assembly.LoadFrom(file.FullName);
foreach (Type type in assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(Plugin)) && type.IsAbstract == false)
{
Plugin b = type.InvokeMember(null,
BindingFlags.CreateInstance,
null, null, null) as Plugin;
plugins.Add(new PluginWrapper(b, file));
b.Register();
}
}
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
if (exSub is FileNotFoundException)
{
FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
sb.AppendLine();
}
string errorMessage = sb.ToString();
Log.Error("Plugins Manager", errorMessage);
}
}
Related
I searched a lot about reloading an assembly at runtime in .NET. The only method I can find is using another AppDomain. But this makes things really complicated. And it is almost impossible in my case because the classes in the assembly which is going to be loaded at runtime do not inherit from MarshalByRefObject. I've looked at Unity game engine. The editor builds the components at runtime and just uses the compiled assembly. How is it possible?
I have done this using MEF. I am not sure if it is an option for you, but it works well. However even with MEF it is somewhat complicated.
On my case I am loading all the dll from a particular folder.
These are the setup classes.
public static class SandBox
{
public static AppDomain CreateSandboxDomain(string name, string path, SecurityZone zone)
{
string fullDirectory = Path.GetFullPath(path);
string cachePath = Path.Combine(fullDirectory, "ShadowCopyCache");
string pluginPath = Path.Combine(fullDirectory, "Plugins");
if (!Directory.Exists(cachePath))
Directory.CreateDirectory(cachePath);
if (!Directory.Exists(pluginPath))
Directory.CreateDirectory(pluginPath);
AppDomainSetup setup = new AppDomainSetup
{
ApplicationBase = fullDirectory,
CachePath = cachePath,
ShadowCopyDirectories = pluginPath,
ShadowCopyFiles = "true"
};
Evidence evidence = new Evidence();
evidence.AddHostEvidence(new Zone(zone));
PermissionSet permissions = SecurityManager.GetStandardSandbox(evidence);
return AppDomain.CreateDomain(name, evidence, setup, permissions);
}
}
public class Runner : MarshalByRefObject
{
private CompositionContainer _container;
private DirectoryCatalog _directoryCatalog;
private readonly AggregateCatalog _catalog = new AggregateCatalog();
public bool CanExport<T>()
{
T result = _container.GetExportedValueOrDefault<T>();
return result != null;
}
public void Recompose()
{
_directoryCatalog.Refresh();
_container.ComposeParts(_directoryCatalog.Parts);
}
public void RunAction(Action codeToExecute)
{
MefBase.Container = _container;
codeToExecute.Invoke();
}
public void CreateMefContainer()
{
RegistrationBuilder regBuilder = new RegistrationBuilder();
string pluginPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
_directoryCatalog = new DirectoryCatalog(pluginPath, regBuilder);
_catalog.Catalogs.Add(_directoryCatalog);
_container = new CompositionContainer(_catalog, true);
_container.ComposeExportedValue(_container);
Console.WriteLine("exports in AppDomain {0}", AppDomain.CurrentDomain.FriendlyName);
}
}
Here is the actual code.
AppDomain domain = SandBox.CreateSandboxDomain($"Sandbox Domain_{currentCount}", directoryName, SecurityZone.MyComputer);
foreach (FileInfo dll in currentDlls)
{
string path = Path.GetFullPath(Path.Combine(directoryName, dll.Name));
if (!File.Exists(path))
File.Copy(dll.FullName, Path.Combine(directoryName, dll.Name), true);
domain.Load(typeof(Runner).Assembly.FullName);
}
You can get the domain back by doing this.
Runner runner = (Runner) domain.CreateInstanceAndUnwrap(typeof(Runner).Assembly.FullName, typeof(Runner).FullName);
runner.CreateMefContainer(); // or runner.Recompose();
You will need to call your code like this.
runner.RunAction(() =>
{
IRepository export = MefBase.Resolve<IRepository>();
export?.Get("123");
Console.WriteLine("Executing {0}", export);
});
Generally speaking, you cannot reload assembly within the same AppDomain. You can create one dynamically and load it (and it will sit in your AppDomain forever), you can load another almost-but-not-quite-the-same copy of your assembly, but once the assembly is in AppDomain, it's stuck.
Imagine that a library assembly defines SomeType and your client code just created an instance. If you unload the library, what is supposed to happen with this instance? If the library is in another AppDomain, the client will use a proxy with a well-defined (in MarshalByRefObject) behaviour (to go zomby with the domain unload and throw exceptions foreverafter). Supporting unloading of arbitrary types would have made the runtime incredibly complicated, unpredictable or both.
As for Unity, see this discussion. Quote:
An "assembly reaload" sounds like some kind of quick update check but in fact the whole scripting environment reloads. This will destroy everything in the managed land. Unity can recover from this by using it's serialization system. Unity serializes the whole scene before the reload, then recreates everything and deserializing the whold scene. Of course only things which can be serialized will "survive" this process.
I'm trying to get a small plugin mechanism running by reflecting an dll file providing my class Plugin (implementing my Plugin-Interface shared among dll and main project / sorry for naming both the same) offering an attribute of type string and a main-method activate:
Interface:
public interface Plugin
{
string pluginName{get;set;}
void activate(System.Windows.Forms.Form main);
}
dll class:
public class Plugin : WhiteA.Plugin
{
public string pluginName{get;set;}
public void activate(System.Windows.Forms.Form main){
//find the right form to modify it
IEnumerable<System.Windows.Forms.ComboBox> ie= GetControlsOfType<System.Windows.Forms.ComboBox>(main);
System.Windows.Forms.ComboBox cb=GetControlsOfType<System.Windows.Forms.ComboBox>(main).FirstOrDefault();
cb.Items.Add("Modification");
System.Windows.Forms.MessageBox.Show(cb.SelectedItem.ToString());
}
public static IEnumerable<T> GetControlsOfType<T>(System.Windows.Forms.Control root)
where T : System.Windows.Forms.Control
{
var t = root as T;
if (t != null)
yield return t;
var container = root as System.Windows.Forms.ContainerControl;
if (container != null)
foreach (System.Windows.Forms.Control c in container.Controls)
foreach (var i in GetControlsOfType<T>(c))
yield return i;
}
}
So here comes the problem, there is no type named "Plugin" to be found in the assembly. Tried to get all types from all assemblies in the directory, get all methods/members/custom attributes from them, have them logged etc, but there is nothing of my class Plugin to be found, while the dll definitely is being found, as it doesn't return the MessageBox.
string[] files=new string[]{};
string path="Error retrieving path";
try{
path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
files = System.IO.Directory.GetFiles(path, "*.dll");
}catch(Exception exF){
}
if(files.Length>0){
foreach (string dll in files){
try{
System.Reflection.Assembly sampleAssembly = System.Reflection.Assembly.LoadFrom(dll);
Type myType = sampleAssembly.GetType("Plugin");
System.Reflection.MethodInfo method = myType.GetMethod("activate");
object myInstance = Activator.CreateInstance(myType);
method.Invoke(myInstance, new object[]{this});
}catch(Exception exL){
}
}
}else{
MessageBox.Show("No working plugins detected in " + path.ToString(), "Nothing to activate", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
}
I know my code probably looks really messy to you all - I think the last try-block is the only thing relevant here, wanted to put in the class itself and the interface for a little bit transparency though - and my english isn't perfect, but I hope someone can help me out finding my attribute+method in the assembly.
EDIT:
try{
System.Reflection.Assembly sampleAssembly = System.Reflection.Assembly.LoadFrom(dll);
List<Type> list= sampleAssembly.GetTypes().Where(p =>
p.Namespace == dll &&
p.Name.Contains("Plugin")
).ToList();
Type myType=list.FirstOrDefault();
//Type myType = sampleAssembly.GetType("Plugin");
System.Reflection.MethodInfo method = myType.GetMethod("activate");
object myInstance = Activator.CreateInstance(myType);
method.Invoke(myInstance, new object[]{this});
}
I did change it according to Getting all types in a namespace via reflection
Still the same result, what did I do wrong?
As pointed out by #stuartd:
Type myType = sampleAssembly.GetType("WhiteA_Plugin_PausedVideo.Plugin");
is the solution, missed the namespace
cb.Items.Add("Modification");
doesn't work though...any suggestions?
Got it to work getting the form's children by Controls["nameOfChild"] directly, that help method to fetch all objects by class seems to be wrong here.
Plugin works now, thanks!
In a MVC controller I use AssemblyLoadContext.Default.LoadFromAssemblyPath(pathToDll); to load an assembly. I want to delete or replace the given .dll file during runtime. This is not possible because the file is not disposed. Is there any way to dispose the .dll file? There are solutions using the AppDomain class, which is not available in asp.net core.
Background:
The user is able to upload a custom .dll file which contains implementations of a given interface. The user should also be able to replace his file. I use the following code in a controller to access the implementations:
var conventions = new ConventionBuilder();
conventions
.ForTypesDerivedFrom<IPluginContract>()
.Export<IPluginContract>()
.Shared();
var configuration = new ContainerConfiguration().WithAssembliesInPath(path, conventions);
using (var container = configuration.CreateContainer())
{
var plugins = container.GetExports<IPluginContract>();
return plugins;
}
With
public static ContainerConfiguration WithAssembliesInPath(
this ContainerConfiguration configuration,
string path, AttributedModelProvider conventions,
SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
var fileNames = Directory
.GetFiles(path, "*.dll", searchOption);
List<Assembly> assemblies = new List<Assembly>();
foreach (string relativePath in fileNames)
{
Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(relativePath));
assemblies.Add(assembly);
}
configuration = configuration.WithAssemblies(assemblies, conventions);
return configuration;
}
OPTION 1:
Try loading dll with method LoadFromStream, then you can remove dll without exceptions.
Ex:
foreach (string relativePath in fileNames)
{
using (var fs = File.Open(relativePath , FileMode.Open))
{
Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(fs);
assemblies.Add(assembly);
}
File.Delete(relativePath); //It doesn't throw exception
}
NOTE: tested with Net Core 3.1 but could work with previous versions.
OPTION 2:
If you have a problem when try to reload assemblies with LoadFromStream you should try to call AssemblyLoadContext.Default.Unload() before to LoadFromStream()
But I'm not sure if it works with AssemblyLoadContext.Default, so if you still keep any exception you should create any class that inherit from AssemblyLoadContext with flag isCollectible to true like this:
public class PluginLoadContext : AssemblyLoadContext
{
public PluginLoadContext() : base(isCollectible: true)
{
}
}
And the code should be:
//var pluginContext = new PluginLoadContext(); //In some place to call unload later
pluginContext.Unload();
foreach (string relativePath in fileNames)
{
using (var fs = File.Open(relativePath , FileMode.Open))
{
Assembly assembly = pluginContext.LoadFromStream(fs);
assemblies.Add(assembly);
}
File.Delete(relativePath); //It doesn't throw exception
}
OPTION 3:
There is another option that override Load method of your custom PluginLoadContext, you only need to load your entry dll, and the reference dll is knew with deps.json file of your entry dll.
In this example is using MemoryStream to prevent attach plugin dll.
public class PluginLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public PluginLoadContext(string pluginPath) : base(isCollectible: true)//isCollectible doesn't appear in netstandard2.1
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly Load(AssemblyName assemblyName)
{
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
//Using MemoryStream to prevent attach dll to this .exe
MemoryStream ms = new MemoryStream();
using (var fs = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
fs.CopyTo(ms);
}
ms.Position = 0;
return LoadFromStream(ms);
}
return null;
}
}
Then you can load your entry plugin dll like this.
var dllPath = "<path to your entry dll>" // dll and deps.json file together .
var pc = new PluginLoadContext(dllPath);
var assembly = pc.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(dllPath)));
//You can load a reference dll too if you need it
var referenceAssembly = pc.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension("<path of reference dll>")));
REF:
https://learn.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support#load-plugins
When you load a dll into your application domain, this dll is not free before the appDomain is being destroyed (i.e. your process is stopped) there is no dispose for a dll.
For references on how to reach your desired functionality please have a look at these questions that are answered already:
Using AppDomain to dynamically load and unload dll
Hot unload and reload of a dll used by an application
It sounds very similar to MEF ( Managed Extensibility Framework ). It allows inject DLL's and also helps to manage the lifecycle.
Example:
public static class MefInjection
{
private static CompositionContainer mycontainer;
public static CompositionContainer MyContainer
{
get
{
if (mycontainer == null)
{
var catalog =
new DirectoryCatalog(".", "MyMEFProject.*");
mycontainer = new CompositionContainer(catalog);
}
return mycontainer;
}
}
}
The preceding code will grab all the exported values from all the assemblies in the same directory starting with "MyMEFProject". Then you can use mycontainer to get loaded DLL's functionality.
From my main C# application i instantiate another application via reflection.
assembly.CreateInstance( ... )
however this other assembly relies on DLLs which are in another directory than the executing assembly. how can i add this directory to the lookup path?
Here is how we implement this need in NDepend.PowerTools. These are a set of tools based on NDepend.API. The DLL NDepend.API.dll is in the directory .\Lib while the NDepend.PowerTools.exe assembly is inn the directory .\.
The NDepend.PowerTools.exe Main() method looks like:
[STAThread]
static void Main() {
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolverHelper.AssemblyResolveHandler;
MainSub();
}
// MainSub() is here to avoids that the Main() method uses something
// from NDepend.API without having registered AssemblyResolveHandler
[MethodImpl(MethodImplOptions.NoInlining)]
static void MainSub() {
...
And the AssemblyResolverHelper class is:
using System;
using System.Diagnostics;
using System.Reflection;
namespace NDepend.PowerTools {
internal static class AssemblyResolverHelper {
internal static Assembly AssemblyResolveHandler(object sender, ResolveEventArgs args) {
var assemblyName = new AssemblyName(args.Name);
Debug.Assert(assemblyName != null);
var assemblyNameString = assemblyName.Name;
Debug.Assert(assemblyNameString != null);
// Special treatment for NDepend.API and NDepend.Core because they are defined in $NDependInstallDir$\Lib
if (assemblyNameString != "NDepend.API" &&
assemblyNameString != "NDepend.Core") {
return null;
}
string binPath =
System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) +
System.IO.Path.DirectorySeparatorChar +
"Lib" +
System.IO.Path.DirectorySeparatorChar;
const string extension = ".dll";
var assembly = Assembly.LoadFrom(binPath + assemblyNameString + extension);
return assembly;
}
}
}
This works within a single AppDomain. I wouldn't use an extra AppDomain here if not needed. AppDomain is a pretty costly facility (in terms of performance) + the thread that jumps the AppDomains boundaries, has to serialize/unserialize in/out data to feed your assembly code, and this can be a headache.
The only advantage of AppDomain is that it lets unload loaded assemblies. So if you expect load/unload assemblies on a regular basis within the life of your main AppDomain, using some extra temporary AppDomain is the way to go.
In my project I need to use plugins. But to use these in my project I need to import an reference of the plugin. Since I can't know how many or which plugins the project uses beforehand I would like to import them dynamically in my project.
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = new IPlugin[pluginFiles.Length];
Assembly asm;
for (int i = 0; i < pluginFiles.Length; i++)
{
string args = pluginFiles[i].Substring(
pluginFiles[i].LastIndexOf("\\") + 1,
pluginFiles[i].IndexOf(".dll") -
pluginFiles[i].LastIndexOf("\\") - 1);
asm = Assembly.LoadFile(pluginFiles[i]);
Type[] types = asm.GetTypes();
In this code example I searched all the .dll files and put them into a string list.
But how can I now load all these .dll files? Or is there a way to use these .dll files without really importing them?
The MEF (Managed Extensibility Framework) Method:
You'll want to add references to System.ComponentModel.Composition to your projects that utilize the import/export functionality of MEF.
First, the bootstrapper/loader (in my case, I just added it to the Main class).
Program.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using MEFContract;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
var prgm = new Program();
// Search the "Plugins" subdirectory for assemblies that match the imports.
var catalog = new DirectoryCatalog("Plugins");
using (var container = new CompositionContainer(catalog))
{
// Match Imports in "prgm" object with corresponding exports in all catalogs in the container
container.ComposeParts(prgm);
}
prgm.DoStuff();
Console.Read();
}
private void DoStuff()
{
foreach (var plugin in Plugins)
plugin.DoPluginStuff();
}
[ImportMany] // This is a signal to the MEF framework to load all matching exported assemblies.
private IEnumerable<IPlugin> Plugins { get; set; }
}
}
The IPlugin interface is the contract between the imports & exports. All plugins will implement this interface. The contract is pretty simple:
IPlugin.cs:
namespace MEFContract
{
public interface IPlugin
{
void DoPluginStuff();
}
}
Finally, you can create as many plugins as you like in different assemblies. They must implement the contract interface and also be decorated with the "Export" attribute to indicate to MEF that they should be matched up with any corresponding imports. Then drop the dlls in a "Plugins" folder (this folder should reside in the same location as the executable). Here's a sample plugin:
Plugin.cs:
using System;
using System.ComponentModel.Composition;
using MEFContract;
namespace Plugin
{
[Export(typeof(IPlugin))]
public class Plugin : IPlugin
{
public void DoPluginStuff()
{
Console.WriteLine("Doing my thing!");
}
}
}
Let's assume for the sake of simplicity that all of the implementations of IPlugin have default constructors (public and no parameters).
That said, you really want to find all types that implement this interface and create an instance of them. You're on the right track somewhat, but you can simplify this tremendously with a little LINQ:
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = (
// From each file in the files.
from file in pluginFiles
// Load the assembly.
let asm = Assembly.LoadFile(file)
// For every type in the assembly that is visible outside of
// the assembly.
from type in asm.GetExportedTypes()
// Where the type implements the interface.
where typeof(IPlugin).IsAssignableFrom(type)
// Create the instance.
select (IPlugin) Activator.CreateInstance(type)
// Materialize to an array.
).ToArray();
That said, you might be better off using a dependency injection framework; they usually allow for dynamic loading and binding to interface implementations in assemblies not referenced at compile time.
Also, while a bit convoluted (in my opinion), you might want to look at the System.AddIn namespaces, as they are built specifically for this purpose. However, the dependency injection route is usually much easier if you don't have to worry about version control of contracts and the like.
I have an application which can not only load plugin at runtime, but also hot-load and unload them as user drop them in the folder, take them out or erase them. So, when I need to recompile my plugin, I don't need to re-launch my application. In my case, all plugin derive from the Plugin abstract, so they are easy to find in .DLL.
Here's my loading method:
private static void LoadPlugins(FileInfo file)
{
try
{
Assembly assembly = Assembly.LoadFrom(file.FullName);
foreach (Type type in assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(Plugin)) && type.IsAbstract == false)
{
Plugin b = type.InvokeMember(null,
BindingFlags.CreateInstance,
null, null, null) as Plugin;
plugins.Add(new PluginWrapper(b, file));
b.Register();
}
}
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
if (exSub is FileNotFoundException)
{
FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
sb.AppendLine();
}
string errorMessage = sb.ToString();
Log.Error("Plugins Manager", errorMessage);
}
}