首先让我们定义几个接口:
// this is your host
public interface IHostController {
// names of all loaded plugins
string[] Plugins { get; }
void StartPlugin(string name);
void StopPlugin(string name);
}
public interface IPlugin {
// with this method you will pass plugin a reference to host
void Init(IHostController host);
void Start();
void Stop();
}
// helper class to combine app domain and loader together
public class PluginInfo {
public AppDomain Domain { get; set; }
public RemoteLoader Loader { get; set; }
}
现在有点重写 RemoteLoader (对我不起作用):
public class RemoteLoader : MarshalByRefObject {
private Assembly _pluginAassembly;
private IPlugin _instance;
private string _name;
public void Init(IHostController host, string assemblyPath) {
// note that you pass reference to controller here
_name = Path.GetFileNameWithoutExtension(assemblyPath);
if (_pluginAassembly == null) {
_pluginAassembly = AppDomain.CurrentDomain.Load(File.ReadAllBytes(assemblyPath));
}
// Required to identify the types when obfuscated
Type[] types;
try {
types = _pluginAassembly.GetTypes();
}
catch (ReflectionTypeLoadException e) {
types = e.Types.Where(t => t != null).ToArray();
}
var type = types.FirstOrDefault(t => t.GetInterface("IPlugin") != null);
if (type != null && _instance == null) {
_instance = (IPlugin) Activator.CreateInstance(type, null, null);
// propagate reference to controller futher
_instance.Init(host);
}
}
public string Name => _name;
public bool IsStarted { get; private set; }
public void Start() {
if (_instance == null) {
return;
}
_instance.Start();
IsStarted = true;
}
public void Stop() {
if (_instance == null) {
return;
}
_instance.Stop();
IsStarted = false;
}
}
还有一个主持人:
// note : inherits from MarshalByRefObject and implements interface
public class HostController : MarshalByRefObject, IHostController {
private readonly Dictionary<string, PluginInfo> _plugins = new Dictionary<string, PluginInfo>();
public void ScanAssemblies(params string[] paths) {
foreach (var path in paths) {
var setup = new AppDomainSetup();
var domain = AppDomain.CreateDomain(Path.GetFileNameWithoutExtension(path), null, setup);
var assemblyPath = Assembly.GetExecutingAssembly().Location;
var loader = (RemoteLoader) domain.CreateInstanceFromAndUnwrap(assemblyPath, typeof (RemoteLoader).FullName);
// you are passing "this" (which is IHostController) to your plugin here
loader.Init(this, path);
_plugins.Add(loader.Name, new PluginInfo {
Domain = domain,
Loader = loader
});
}
}
public string[] Plugins => _plugins.Keys.ToArray();
public void StartPlugin(string name) {
if (_plugins.ContainsKey(name)) {
var p = _plugins[name].Loader;
if (!p.IsStarted) {
p.Start();
}
}
}
public void StopPlugin(string name) {
if (_plugins.ContainsKey(name)) {
var p = _plugins[name].Loader;
if (p.IsStarted) {
p.Stop();
}
}
}
}
现在让我们创建两个不同的程序集。它们中的每一个只需要引用接口IPlugin 和 IHostController。在第一个程序集中定义插件:
public class FirstPlugin : IPlugin {
const string Name = "First Plugin";
public void Init(IHostController host) {
Console.WriteLine(Name + " initialized");
}
public void Start() {
Console.WriteLine(Name + " started");
}
public void Stop() {
Console.WriteLine(Name + " stopped");
}
}
在第二个程序集中定义另一个插件:
public class FirstPlugin : IPlugin {
const string Name = "Second Plugin";
private Timer _timer;
private IHostController _host;
public void Init(IHostController host) {
Console.WriteLine(Name + " initialized");
_host = host;
}
public void Start() {
Console.WriteLine(Name + " started");
Console.WriteLine("Will try to restart first plugin every 5 seconds");
_timer = new Timer(RestartFirst, null, 5000, 5000);
}
int _iteration = 0;
private void RestartFirst(object state) {
// here we talk with a host and request list of all plugins
foreach (var plugin in _host.Plugins) {
Console.WriteLine("Found plugin " + plugin);
}
if (_iteration%2 == 0) {
Console.WriteLine("Trying to start first plugin");
// start another plugin from inside this one
_host.StartPlugin("Plugin1");
}
else {
Console.WriteLine("Trying to stop first plugin");
// stop another plugin from inside this one
_host.StopPlugin("Plugin1");
}
_iteration++;
}
public void Stop() {
Console.WriteLine(Name + " stopped");
_timer?.Dispose();
_timer = null;
}
}
现在在托管所有插件的主 .exe 中:
static void Main(string[] args) {
var host = new HostController();
host.ScanAssemblies(@"path to your first Plugin1.dll", @"path to your second Plugin2.dll");
host.StartPlugin("Plugin2");
Console.ReadKey();
}
输出是:
First Plugin initialized
Second Plugin initialized
Second Plugin started
Will try to restart first plugin every 5 seconds
Found plugin Plugin1
Found plugin Plugin2
Trying to start first plugin
First Plugin started
Found plugin Plugin1
Found plugin Plugin1
Found plugin Plugin2
Trying to stop first plugin
Found plugin Plugin2
Trying to stop first plugin
First Plugin stopped
First Plugin stopped
Found plugin Plugin1
Found plugin Plugin2
Trying to stop first plugin