感谢当前的帖子,我想我有一个解决方案,这个帖子及其接受的答案:AppDomain.Load() fails with FileNotFoundException
首先,我认为您应该使用接口代替基类作为您的处理程序。接口应该在基类上声明,然后你只能使用它。
解决方案:在共享程序集中创建一个具体类型,它继承自MarshalByRefObject,并实现您的服务器接口。这个具体类型是一个代理,可以在 AppDomain 之间进行序列化/反序列化,因为您的主应用程序知道它的定义。您不再需要从类ServerBase 中的MarshalByRefObject 继承。
// - MUST be serializable, and MUSNT'T use unknown types for main App
[Serializable]
public class Query
{
...
}
public interface IServerBase
{
string Execute(Query q);
}
public abstract class ServerBase : IServerBase
{
public abstract string Execute(Query q);
}
// Our CUSTOM PROXY: the concrete type which will be known from main App
[Serializable]
public class ServerBaseProxy : MarshalByRefObject, IServerBase
{
private IServerBase _hostedServer;
/// <summary>
/// cstor with no parameters for deserialization
/// </summary>
public ServerBaseProxy ()
{
}
/// <summary>
/// Internal constructor to use when you write "new ServerBaseProxy"
/// </summary>
/// <param name="name"></param>
public ServerBaseProxy(IServerBase hostedServer)
{
_hostedServer = hostedServer;
}
public string Execute(Query q)
{
return(_hostedServer.Execute(q));
}
}
注意:为了发送和接收数据,IServer中声明的每个类型必须是可序列化的(例如:带有[Serializable]属性)
然后,您可以使用上一个链接“Loader class”中找到的方法。
这是我修改后的 Loader 类,它在共享程序集中实例化具体类型,并为每个插件返回一个代理:
/// <summary>
/// Source: https://stackoverflow.com/questions/16367032/appdomain-load-fails-with-filenotfoundexception
/// </summary>
public class Loader : MarshalByRefObject
{
/// <summary>
/// Load plugins
/// </summary>
/// <param name="assemblyName"></param>
/// <returns></returns>
public IPlugin[] LoadPlugins(string assemblyPath)
{
List<PluginProxy> proxyList = new List<PluginProxy>(); // a proxy could be transfered outsite AppDomain, but not the plugin itself ! https://stackoverflow.com/questions/4185816/how-to-pass-an-unknown-type-between-two-net-appdomains
var assemb = Assembly.LoadFrom(assemblyPath); // use Assembly.Load if you want to use an Assembly name and not a path
var types = from type in assemb.GetTypes()
where typeof(IPlugin).IsAssignableFrom(type)
select type;
var instances = types.Select(
v => (IPlugin)Activator.CreateInstance(v)).ToArray();
foreach (IPlugin instance in instances)
{
proxyList.Add(new PluginProxy(instance));
}
return (proxyList.ToArray());
}
}
然后,在主应用程序中,我也使用“dedpichto”和“James Thurley”的代码来创建AppDomain,实例化和调用Loader类。然后我可以使用我的代理,因为它是我的插件,因为 .NET 由于MarshalByRefObject 创建了一个“透明代理”:
/// <see cref="https://stackoverflow.com/questions/4185816/how-to-pass-an-unknown-type-between-two-net-appdomains"/>
public class PlugInLoader
{
/// <summary>
/// https://stackoverflow.com/questions/16367032/appdomain-load-fails-with-filenotfoundexception
/// </summary>
public void LoadPlugins(string pluginsDir)
{
// List all directories where plugins could be
var privatePath = "";
var paths = new List<string>();
List<DirectoryInfo> dirs = new DirectoryInfo(pluginsDir).GetDirectories().ToList();
dirs.Add(new DirectoryInfo(pluginsDir));
foreach (DirectoryInfo d in dirs)
privatePath += d.FullName + ";";
if (privatePath.Length > 1) privatePath = privatePath.Substring(0, privatePath.Length - 1);
// Create AppDomain !
AppDomainSetup appDomainSetup = AppDomain.CurrentDomain.SetupInformation;
appDomainSetup.PrivateBinPath = privatePath;
Evidence evidence = AppDomain.CurrentDomain.Evidence;
AppDomain sandbox = AppDomain.CreateDomain("sandbox_" + Guid.NewGuid(), evidence, appDomainSetup);
try
{
// Create an instance of "Loader" class of the shared assembly, that is referenced in current main App
sandbox.Load(typeof(Loader).Assembly.FullName);
Loader loader = (Loader)Activator.CreateInstance(
sandbox,
typeof(Loader).Assembly.FullName,
typeof(Loader).FullName,
false,
BindingFlags.Public | BindingFlags.Instance,
null,
null,
null,
null).Unwrap();
// Invoke loader in shared assembly to instanciate concrete types. As long as concrete types are unknown from here, they CANNOT be received by Serialization, so we use the concrete Proxy type.
foreach (var d in dirs)
{
var files = d.GetFiles("*.dll");
foreach (var f in files)
{
// This array does not contains concrete real types, but concrete types of "my custom Proxy" which implements IPlugin. And here, we are outside their AppDomain, so "my custom Proxy" is under the form of a .NET "transparent proxy" (we can see in debug mode) generated my MarshalByRefObject.
IPlugin[] plugins = loader.LoadPlugins(f.FullName);
foreach (IPlugin plugin in plugins)
{
// The custom proxy methods can be invoked !
string n = plugin.Name.ToString();
PluginResult result = plugin.Execute(new PluginParameters(), new PluginQuery() { Arguments = "", Command = "ENUMERATE", QueryType = PluginQueryTypeEnum.Enumerate_Capabilities });
Debug.WriteLine(n);
}
}
}
}
finally
{
AppDomain.Unload(sandbox);
}
}
}
确实很难找到可行的解决方案,但我们终于可以将具体类型的自定义代理实例保存在另一个 AppDomain 中,并像在主应用程序中一样使用它们。
希望这(巨大的答案)有所帮助!