我想提供一些在我的软件中创builddynamic加载插件的方法。 典型的方法是使用LoadLibrary WinAPI函数加载一个dll并调用GetProcAddress来获取指向该dll内部函数的指针。
我的问题是如何在C#/ .Net应用程序中dynamic加载插件?
下面的代码片断(C#)构造了从应用程序路径中的类库(* .dll)中的Base
派生的任何具体类的实例,并将它们存储在一个列表中。
using System.IO; using System.Reflection; List<Base> objects = new List<Base>(); DirectoryInfo dir = new DirectoryInfo(Application.StartupPath); foreach (FileInfo file in dir.GetFiles("*.dll")) { Assembly assembly = Assembly.LoadFrom(file.FullName); foreach (Type type in assembly.GetTypes()) { if (type.IsSubclassOf(typeof(Base)) && type.IsAbstract == false) { Base b = type.InvokeMember(null, BindingFlags.CreateInstance, null, null, null) as Base; objects.Add(b); } } }
编辑: 马特提到的类可能是一个更好的选择在.NET 3.5中。
从.NET 3.5开始,就有了一种正式的,可以从.NET应用程序创建和加载插件的方法。 这一切都在System.AddIn命名空间中。 有关更多信息,可以查看MSDN上的这篇文章: 加载项和扩展性
有关如何动态加载.NET程序集的信息,请参阅此问题 (和我的答案 )。 下面是一些代码,用于加载创建AppDomain
并将程序集加载到其中。
var domain = AppDomain.CreateDomain("NewDomainName"); var pathToDll = @"C:\myDll.dll"; var t = typeof(TypeIWantToLoad); var runnable = domain.CreateInstanceFromAndUnwrap(pathToDll, t.FullName) as IRunnable; if (runnable == null) throw new Exception("broke"); runnable.Run();
插件框架的典型要求是卸载插件。 要卸载动态加载的程序集(例如插件和加载项),必须卸载包含的AppDomain
。 有关更多信息,请参阅卸载AppDomains上的MSDN上的这篇文章 。
有一个堆栈溢出问题和答案 ,描述如何使用Windows通信框架(WCF)创建一个插件框架。
我知道两个插件框架:
System.AddIn
名称空间。 有些人将管理扩展性框架(MEF)作为插件或插件框架讨论,事实并非如此。 有关更多信息,请参阅此StackOverflow.com问题和此StackOverflow.com问题 。
一个技巧是将所有插件等加载到自己的AppDomain中,因为运行的代码可能是恶意的。 自己的AppDomain也可以用来“过滤”你不想加载的程序集和类型。
AppDomain domain = AppDomain.CreateDomain("tempDomain");
并将程序集加载到应用程序域中:
AssemblyName assemblyName = AssemblyName.GetAssemblyName(assemblyPath); Assembly assembly = domain.Load(assemblyName);
要卸载应用程序域:
AppDomain.Unload(domain);
是的,++到Matt和System.AddIn(有关System.AddIn的MSDN杂志的两篇文章可在这里和这里找到 )。 您可能希望了解.NET Framework将来可能发展的另一项技术,即Codeplex上的CTP版本中提供的托管扩展性框架 。
基本上你可以用两种方法做到这一点。
首先是导入kernel32.dll并使用之前使用的LoadLibrary和GetProcAddress:
[DllImport("kernel32.dll")] internal static extern IntPtr LoadLibrary(String dllname); [DllImport("kernel32.dll")] internal static extern IntPtr GetProcAddress(IntPtr hmodulee, String procname);
其次是以.NET方式进行:使用反射。 检查System.Reflection命名空间和以下方法:
首先通过它的路径加载程序集,然后通过它的名字得到类型(class),然后再次通过它的名字得到类的方法,最后调用带有相关参数的方法。
这篇文章有点旧,但是仍然适用于在应用程序中创建一个扩展性层:
让用户使用宏和插件为.NET应用程序添加功能