我正在用C#创建一个插件框架。该框架的主要要求是在运行时加载,卸载和更新插件。
为实现这一目标,我一直在创建AppDomain并将插件程序集加载到AppDomains中。
在Windows上的Microsoft .NET上一切正常但插件不适用于在mac或linux上运行的单声道。
尝试启动插件时,我得到一个像这样的例外:
无法转换类型为'System.Func`1 [[API.Network.NodeType,API,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null]]的参数0,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089'键入'System.Func`1 [[API.Network.NodeType,API,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null]],mscorlib,Version = 4.0.0.0,Culture =中立,PublicKeyToken = b77a5c561934e089'
这是因为每个插件都有自己的API.dll程序集副本,虽然程序集是一个相同的副本,但mono看不到类型是相同的。
如何从主应用程序的目录中获取加载API.dll的插件?或者,或者,如何让单声道看到类型是相同的?
答案 0 :(得分:1)
为了找到你的问题的答案我创建了一个简单的插件系统并在Windows下成功地在单声道3.2.3上进行了测试(遗憾的是我现在无法在Linux上进行测试,也许明天就可以)。我的代码:
<强> SDK.dll 强>
using System;
namespace SDK
{
public interface IPlugin
{
void SomeMethod();
SomeSDKType GetSDKType();
}
}
using System;
using System.Collections.Generic;
namespace SDK
{
[Serializable]
public class StringEventArgs : EventArgs
{
public string Message { get; set; }
}
public class SomeSDKType : MarshalByRefObject
{
public event EventHandler<StringEventArgs> SDKEvent;
public Action SDKDelegate;
public void RiseSDKEvent(string message)
{
var handler = SDKEvent;
if (handler != null) SDKEvent(this, new StringEventArgs { Message = message });
}
public Dictionary<int, string> GetDictionary()
{
var dict = new Dictionary<int, string> ();
dict.Add(1, "One");
dict.Add(2, "Two");
return dict;
}
}
}
<强> Plugin.dll 强>
using System;
using SDK;
namespace Plugin
{
public class Plugin : MarshalByRefObject, IPlugin
{
public Plugin()
{
}
public void SomeMethod()
{
Console.WriteLine("SomeMethod");
}
public SomeSDKType GetSDKType()
{
var obj = new SomeSDKType();
obj.SDKDelegate = () => Console.WriteLine("Delegate called from {0}", AppDomain.CurrentDomain.FriendlyName);
return obj;
}
}
}
托管计划
using System;
using System.Reflection;
using System.IO;
using SDK;
namespace AppDomains
{
class MainClass
{
public static void Main(string[] args)
{
var domain = AppDomain.CreateDomain("Plugin domain"); // Domain for plugins
domain.Load(typeof(IPlugin).Assembly.FullName); // Load assembly containing plugin interface to domain
var currentPath = Directory.GetCurrentDirectory();
var pluginPath = Path.Combine(currentPath, "Plugins");
var pluginFiles = Directory.GetFiles(pluginPath, "*.dll");
foreach (var pluginFile in pluginFiles) // Foreach dll in Plugins directory
{
var asm = Assembly.LoadFrom(pluginFile);
foreach (var exportedType in asm.GetExportedTypes())
{
if (!typeof(IPlugin).IsAssignableFrom(exportedType)) continue; // Check if exportedType implement IPlugin interface
domain.Load(asm.FullName); // If so load this dll into domain
var plugin = (IPlugin)domain.CreateInstanceAndUnwrap(asm.FullName, exportedType.FullName); // Create plugin instance
plugin.SomeMethod(); // Call plugins methods
var obj = plugin.GetSDKType();
obj.SDKDelegate();
var dict = obj.GetDictionary();
foreach (var pair in dict)
{
Console.WriteLine("{0} - {1}", pair.Key, pair.Value);
}
obj.SDKEvent += obj_SDKEvent;
obj.RiseSDKEvent(string.Format("Argument from domain {0}", AppDomain.CurrentDomain.FriendlyName));
}
}
Console.ReadLine();
}
static void obj_SDKEvent(object sender, StringEventArgs e)
{
Console.WriteLine("Received event in {0}", AppDomain.CurrentDomain.FriendlyName);
Console.WriteLine(e.Message);
}
}
}
<强>的App.config 强>
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Plugins"/>
</assemblyBinding>
</runtime>
</configuration>
对代码的一些解释。我用插件界面创建了SDK dll。所有插件和主机应用程序都必须引用它。必须在没有SDK dll的情况下提供插件,因为主机应用已包含它。它们放入主机应用程序目录中的插件目录(即,如果app path = c:\ MyApp ,则插件位于 c:\ MyApp \ Plugins 因此,为了提供CLR(或单声道)机会来查找插件,我还使用探测元素创建了App.config文件。
希望这有帮助。