First of all, hello 0x00sec I’m back, after around 2 years I finally am not head deep in research, school, and work, and I’ve been working on a plugin loader called Code_Olive, more on that in a bit, Code_Olive is meant to be my solution to the need for a new and better PluginLoader for .Net Core and even .Net Framework But is there a better way to load a plugin?
In .Net based languages specifically c#, f# and vb there are many many ways of making a plugin type system, from reflection to dynamic interface loading. there are many options each with there ups and downs but what’s the best way to load a plugin?
Currently, there are only 2 Major ways that I know work of isolating a plugin from the host and other plugins.
- Reflection using the System.Reflection.Loader library from Microsoft on NuGet allows us to take full control of how a loaded assembly is loaded, bound, and how externals are resolved allowing us to fully control what environment our Plugin is held in but it takes more code and more knowledge of the .Net Runtime to create a robust and efficient AssemblyLoadContext but only supports Assembly Unloading in 3.0.
- App domains Curates this by making plugin stuck in there own virtual environment unable to directly access the main program runtime but also has its downsides since we can’t control what gets loaded by the plugin it can still load host assemblies witch we might not want. This also has the added benefit of making plugins unloadable, it also is not supported in .Net Core.
When it comes to reading and executing plugin data, we again have 3 ways
- Plugin Interfacing, this requires an SDK of sorts to be set up between the Plugin and Host Assemblies making isolation difficult as some data will need to be accessed by both assemblies.
- Reflection, this does not require an SDK but is more difficult to create and can be slower if not optimized/done incorrectly.
- Dependency injection, it’s fast but bulky and annoying to set up.
we have all these methods of loading plugins but what why?
If we using System.Reflection.Loader we can enforce a partial-custom core library and restrict access to potentially dangerous assemblies but using this alone will require us to use an SDK assembly to be shared between both environments however we can do better Method Injection a topic which I feel is seen as black magic or taboo, we all know that we can create a delegate off of a method adding a short of 0.2-0.5 ms ontop of direct execution even if its a static method but not everyone knows that if they simply replace the runtime handle for the method they can replace the method executed. well doing so, the result is we can replace functions inside the plugin assembly without adding any bulk unless you manage it and not have any added processing time, interested? This is the exact idea I had in mind when it comes to Code_Olive now I’m still working on it and will soon work on creating a .Net Framework and .Net Core branch to allow for unloading but the way it works currently is I load the assembly using Assembly.LoadFile witch isolates the assembly and blocks it from causing other assemblies to load from it, then using standard Reflection methods I look for a class called ‘CodeOliveInfo’ witch, as the name suggests, contains all the plugin info such as its name and version but it also contains a few nice things. have ever wanted to edit how your assembly is loaded and executed at runtime? well this is what using these methods can provide, using 3 methods in the plugin and a lot of code in the main program I have created stages to loading, stage 1 loading your Functions and Tweaking what you want to load, stage 2 initializing the actual plugin by reading data from other plugins and doing some initial processing, stage 3 actually starting your plugin. these 3 allow the plugin to have full control over what it does and when it comes to communication between host programs and other plugins we have Method Injection using an attribute that contains the Plugin name or a Core Library’s and the full path to the method we can effectively directly access whichever functions we need while still being able to block what plugins can access without any delay to executing such functions either!
awesome right or wrong? that’s why this is a discussion. do you think this would be worse or better when it comes to plugin loading and executing? and if you have a better idea of how to do it, what is it?