-
Notifications
You must be signed in to change notification settings - Fork 60
Plugins
Kuriimu2 utilizes DLLs as plugins to extend its file format support.
Those DLLs are written in C# and loaded at runtime using MEF (Microsoft Extensibility Framework). MEF reduces the code required to load dlls at runtime, making it much easier to load them. Additionally it allows loading metadata for a plugin, instead of initializing it, which makes it faster to get metadata and more memory efficient. To make use of MEFs capabilities in code, one must first reference the System.CompositionModel assembly.
To prepare a plugin project in Visual Studio Community 2017, first you must set up a custom nuget package source.
- Go to
Tools > NuGet Package Manager > Package Manager Settings > Package Sources
. - Add a new package source called "Kuriimu2".
- Set the source directory to "[Kuriimu2RepoDir]\nuget".
If you have not yet build the necessary nuget packages, go into "[Kuriimu2RepoDir]\nuget\BuildNugets" and run the included nugetPack.Debug.linq
in LinqPad v5.
Then when creating your plugin, simply install the needed nugets. You will always need at least Kuriimu2.Kontract
and then most likely Kuriimu2.Komponent
if you're going to be dealing with files.
The first type of plugin is the widely used file-based one. It's a plugin that loads a given file and interprets it into a more general and/or readable structure for either the UI or other programs.
Coding a basic file plugin for Kuriimu2 is just combining different interfaces depending on its needs.
The general implementation order for plugin interfaces goes as follows:
- Type (
top level
):ITextAdapter
,IImageAdapter
,IArchiveAdapter
,etc...
- File Capability:
IIdentifyFiles
,ICreateFiles
,ILoadFiles
,ISaveFiles
- Type Functionality:
IAddEntries
,IImportImages
,IAddFiles
,etc...
This is for style, readability and consistency between plugins.
You should normally only implement a single top level
interface per plugin definition. This helps keep the plugins purpose specific without bloating Identify()
and Load()
with tons of type checking when two separate plugins would be much cleaner.
You should only include the interfaces that your plugin can properly support. An example of incorrect usage would be to implement IIdentifyFiles
but then have the Identify()
function do a poor job where it returns false-positives very often for files that the plugin doesn't actually deal with.
If a plugin does not identify files (since it doesn't implement IIdentifyFiles
), it will be treated by Kore as a Blind Plugin. This will make it a candidate for manual plugin loading and also list it in the event arguments of the IdentificationFailed event.
To make use of Kuriimu2's UI you need to implement one of the top level
interfaces. This also requires that you export the interface type via the MEF Export attribute from the System.CompositionModel assembly. Plugins that don't export the interface will be ignored by Kore since all plugins are loaded via MEF imports.
Information about PluginInfo
and PluginExtensionInfo
can be found here.
Example:
using System.CompositionModel;
[Export(typeof(Plugin))] // Exporting the Plugin itself makes it available for direct referencing via MEF by other plugins.
// [Export(typeof(IImageAdapter))]
// Without exporting IImageAdapter, Kore won't know that this plugin is actually an image plugin
// and thus the Kuriimu2 UI won't load the plugin at all.
[Export(typeof(IIdentifyFiles))] // Export IIdentifyFiles so Kore allows auto-selection of this plugin.
[Export(typeof(ILoadFiles))] // Export ILoadFiles so Kore allows this plugin to receive loaded files.
[PluginInfo("GUID", "Name", "ShortName", "Author", "Website", "Description")]
[PluginExtensionInfo("*.ext")] // Defines the extension(s) that this plugin looks for by default.
public class Plugin : IImageAdapter, IIdentifyFiles, ILoadFiles
{
public IList<BitmapInfo> BitmapInfos { get; }
public bool Identify(string filename)
{
// Here the file gets read, to identify it and to check if this plugin can handle it.
// Not inheriting from IIdentifyFiles makes Kore treat this plugin as a Blind Plugin.
}
public void Load(string filename)
{
// Here the file gets read and loaded to provide the necessary data to the output of the interface.
// In this case Load should populate the BitmapInfos list.
}
public void Dispose()
{
// Every interface from Kontract inherits from IDisposable and therefore each plugin instance is disposable.
}
}