I'm working on a sort of plugin system for a Discord bot. Right now i'm on macOS using Mono as my runtime and I've been looking at using seperate AppDomains to load and manage each of the plugins. The plugins themselves do have dependencies but these are already loaded in the main appdomain. So here lies my problem:
I've attempted to iterate over the loaded assemblies in the current domain and load them into my separate AppDomain so the plugin's dependencies are already loaded in. This results in a FileNotFoundException when trying to load said dependencies (even though I point them to the correct path in the exe directory).
I've tried without doing that and just going through and loading the plugin dll's into a separate AppDomain and seeing if it'll resolve automatically. Still throws a FileNotFoundException.
I tried manually creating an array in order of the dependendies (in my application's case, Newtonsoft.Json -> DSharpPlus -> Bot.CommandManager). I can't even load Newtonsoft.Json due to a missing dependency.
Now, I'm loading the raw bytes into a stream reader and passing that into AppDomain.Load to make sure that the FileNotFoundException comes from a lack of dependency and not the actual file being missing. The file is there, still throws FileNotFoundException on
domain.Load
The only way I've been able to successfully load the plugins is just using Assembly.Load
and not worrying about AppDomains which works great until I update something and want to unload/reload the plugin. Which is where I need to use AppDomain. I'm kind of lost, here's my current iteration of the code. It's probably extremely unnecessary to have a separate AppDomain for each plugin but I'm rolling with that for now.
private void SetupAppDomain()
{
string path = Directory.GetCurrentDirectory() + "/modules/";
Console.WriteLine(path);
List<string> installedModules = new List<string>();
string[] files = Directory.GetFiles(path);
foreach(string modulePath in files)
{
try
{
AppDomain domain = AppDomain.CreateDomain(Path.GetFileName(modulePath), AppDomain.CurrentDomain.Evidence);
StreamReader reader = new StreamReader(modulePath, System.Text.Encoding.GetEncoding(1252), false);
byte[] b = new byte[reader.BaseStream.Length];
reader.BaseStream.Read(b, 0, System.Convert.ToInt32(reader.BaseStream.Length));
domain.Load(b); //this throws the FileNotFoundException
Assembly[] a = domain.GetAssemblies();
int index = 0;
for (int i = 0; i < a.Length; i++)
{
if(a[i].GetName().Name + ".dll" == Path.GetFileName(modulePath))
{
index = i;
break;
}
}
installedModules.Add(a[index].GetName().Name);
}
catch(Exception ex)
{
throw ex;
}
}
}
Worst case scenario, I can just go back to the old way of doing it and just have to restart the bot when I want to reload assemblies.