2

I'm learning C# and am researching how to allow people to write plugins for an app I'm writing.

To start, I publish an API (a dll with interfaces) that their code must adhere to.

Now, I'm trying to understand how to work with their code. I've written a test plugin, built to a dll, and put it into a "plugins" directory that my script is watching.

I'm just not sure what to do next.

Since the API interfaces are shared my app knows what to expect. For example, they should have a main class which interfaces a Plugin interface.

// Example api interface:
public interface Plugin {
    void Initialize();
}

// Example of their code:
public class TestPlugin : Plugin {
    public void Initialize() {
      // ... do stuff
    }
}

My question is, how can I instantiate their TestPlugin, so that I can properly call Initialize and any other methods?

I have some ideas but am still too new to C# and don't want to jump the gun.

helion3
  • 34,737
  • 15
  • 57
  • 100
  • 1
    Firstly, it's best practice to prefix your interface name with a capital I e.g. `IPlugin`. Secondly, plugins are often loaded by scanning assemblies for types that implement your required interface. – DavidG Feb 09 '17 at 01:35
  • Other problems to think about are plugin versioning and plugin isolation. Do you plan on having backward compatibility with your plugins? Do you trust your plugins to run in your app domain, or should they be running in their own app domain with a lower security trust? All of these factors should be considered early, so you can pick the proper technology to use. – John Koerner Feb 09 '17 at 04:24
  • I would prefer to isolate them as much as possible but this is for a game, so overall there are fewer sensitive areas compared to an application. I would appreciate any links to good resources on doing what you're referring to. – helion3 Feb 09 '17 at 07:20

2 Answers2

0

The best way to do this would be to use MEF (Managed Extensibility Framework), otherwise known as the System.ComponentModel.Composition library.

If you did this, the library writer would put the following line above their class:

[Export(typeof(Plugin))]

Then you create some MEF classes to import any plugins. Start with a DirectoryCatalog since you are loading from a folder:

DirectoryCatalog pluginDir = new DirectoryCatalog("Plugins");
CompositionContainer mefContainer = new CompositionContainer(pluginDir);

Afterwards create a CompositionContainer from your catalog (shown above). Now you can have a class member marked with ImportMany like so:

[ImportMany]
private List<Plugin> plugins;

and call ComposeParts on the container, this will auto-populate your list with any exported classes found. Alternatively, you can directly ask for exports of a given type:

IEnumerable<Plugin> plugins = mefContainer.GetExportedValues<Plugin>();

One thing to note when using MEF, you get one, and only one, instance of each plugin. If you want multiple instances for some reason, have your users export a Factory.

If you want to go the hard way, you could load the assembly manually using Assembly.Load and then reflection to try and find the types implementing your interface. MEF does this work for you, so I would go with that.

BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
  • This sounds like precisely what I want and it does sound more robust than doing it all manually. I'll try this out and once I get it working I'll mark this as accepted. One question though, I must be doing something wrong because the [Something] syntax has never worked for me, it gets marked as invalid syntax. – helion3 Feb 09 '17 at 01:43
  • @helion3 Attributes aren't anything special (at least since I can remember). Do make sure you have the correct using statements (VS will usually showy you a lightbulb hint if you are missing a reference or namespace). Also, make sure the attribute is above a valid target, some are only good on methods, others classes, etc. – BradleyDotNET Feb 09 '17 at 01:45
  • I'm using MonoDevelop at the moment, all it says is that "Export" does not exist so I'm missing an import of some sort. The docs say it's under `System.ComponentModel.Composition` which is what you mentioned, but that's not available to me. – helion3 Feb 09 '17 at 01:47
  • Looks like unity/mono is using .NET 2.0 and this was added to .NET 4.0... arg – helion3 Feb 09 '17 at 01:53
  • @helion3 Yeah, doing any of this on .NET 2 will likely be very painful. I'm not even sure how much reflection that version had – BradleyDotNET Feb 09 '17 at 06:19
  • I've been successful with the method @pm100 mentioned. It works well enough for now, so I can't say whether it'll give me trouble down the road, but until unity updates there's not much I can do. – helion3 Feb 09 '17 at 07:21
0

you need to find assemblies , load them and look for classes that implement IPlugin (please use Ixxx for interfaces)

There are helper libraries that do this for you although they feel over complex to me. MEF is the best known https://msdn.microsoft.com/en-us/library/dd460648(v=vs.110).aspx

If you want to roll your own.

  • Enumerate the 'plugins' directory for all .dll files
  • do an assembly.load on each one
  • enumerate the types and see if any classes supports IPLugin
  • if so do an activator.createinstance

good luck

pm100
  • 48,078
  • 23
  • 82
  • 145
  • Not sure 4 lines of code is "overly complex" at least in the case of MEF... – BradleyDotNET Feb 09 '17 at 01:42
  • 1
    in all projects that i started out using MEF with I eventually gave up and did my own. By 'complex' I dont mean hard to code to , I mean too many fancy features, but then never had the exact fanciness I wanted – pm100 Feb 09 '17 at 01:50
  • I'd echo what pm100 said. I dropped MEF and went with a custom solution. MEF probably works well if you want to structure your entire codebase as a "dependency soup" and let it take care of establishing relationships between all your components in the entire system. That's often not what you want with a plugin system though, where you usually want a thin, very specific layer of communication between systems. I found MEF a poor fit for this. MEF also proved too opaque when trying to recover from composition errors. – Roger Sanders Feb 09 '17 at 02:52
  • I've accepted this answer because it's what I've gone with - but I didn't even get a choice of MEF because this particular app is in Unity 5 which is still on Mono/.NET 2.0, so MEF isn't available. The listed steps are precisely what I've done. – helion3 Feb 09 '17 at 07:22