//This attempts to prevent the program from crashing if a plugin throws an exception. //It is a rather dirty approach and repeated calls will cause a memory leak. private void PreventPluginCrash(object sender, UnhandledExceptionEventArgs e) { StackTrace trace = new StackTrace((Exception)e.ExceptionObject); foreach (StackFrame frame in trace.GetFrames()) { Assembly frameAssembly = frame.GetMethod().DeclaringType.Assembly; if (Plugins.ContainsKey(frameAssembly.GetName().Name)) { IMSPluginBase plugin = Plugins[frameAssembly.GetName().Name]; Logger.WriteError("An uncaught exception was raised in plugin " + plugin.Name + "! The plugin has been disabled. Error:\n" + e.ExceptionObject); try { plugin.Stop(); } catch (Exception stopException) { Logger.WriteError("During fatal error unload, the plugin's stop method also failed. Exception:\n" + stopException); } Plugins.Remove(plugin.Name); KnownPlugins[plugin.PluginAssembly.GetName().Name].Enabled = false; Thread.CurrentThread.IsBackground = true; Thread.CurrentThread.Join(); //suspend the thread indefinitely to stop the CLR from shutting down } } Logger.WriteError("A fatal error occured, resulting in IMS shutting down!\n" + e.ExceptionObject); }
/// <summary> /// Attempts to load a plugin into memory from the specified path. /// </summary> /// <param name="path">The path of the plugin to load.</param> /// <returns>The loaded plugin.</returns> public IMSPluginBase LoadPlugin(string path) { IMSPluginBase plugin = LoadPluginAssembly(path); plugin.Start(); return(plugin); }
/// <summary> /// Creates a new instance of the plugin information class using the given plugin. /// </summary> /// <param name="plugin">The plugin to gather information about.</param> public PluginInformation(IMSPluginBase plugin) { Name = plugin.Name; Author = plugin.Author; Description = plugin.Description; AssemblyName = plugin.PluginAssemblyName; FileName = plugin.PluginAssembly.Location; AssemblyVersion = plugin.CurrentVersion; Enabled = true; }
/// <summary> /// Attempts to remove a plugin from memory. This does not guarantee that the plugin's assembly will be unloaded. /// </summary> /// <param name="plugin">The plugin to unload.</param> public void UnloadPlugin(IMSPluginBase plugin) { lock (Locker) if (Plugins.ContainsKey(plugin.PluginAssemblyName)) { plugin.Stop(); Plugins.Remove(plugin.PluginAssemblyName); } else { throw new ArgumentException("This plugin object has not been loaded by the plugin manager."); } }
private IMSPluginBase LoadPluginAssembly(string path) { lock (Locker) { AssemblyLoadContext context = new AssemblyLoadContext(null, true); Assembly pluginAssembly = context.LoadFromAssemblyPath(path); try { IMSPluginBase plugin = (IMSPluginBase)Activator.CreateInstance(pluginAssembly.GetTypes().Where(x => x.BaseType == typeof(IMSPluginBase)).Single()); Plugins[plugin.PluginAssemblyName] = plugin; KnownPlugins[plugin.PluginAssemblyName] = new PluginInformation(plugin); return(plugin); } finally { context.Unload(); //start context unload now so the assembly disappears once the plugin is gc'd } } }