windows 如何在 .NET 中加载插件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14278/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to load plugins in .NET?
提问by Micha? Piaskowski
I'd like to provide some way of creating dynamically loadable plugins in my software. Typical way to do this is using the LoadLibraryWinAPI function to load a dll and calling GetProcAddressto get an pointer to a function inside that dll.
我想提供一些在我的软件中创建动态可加载插件的方法。执行此操作的典型方法是使用LoadLibraryWinAPI 函数加载 dll 并调用GetProcAddress以获取指向该 dll 中的函数的指针。
My question is how do I dynamically load a plugin in C#/.Net application?
我的问题是如何在 C#/.Net 应用程序中动态加载插件?
采纳答案by Zooba
The following code snippet (C#) constructs an instance of any concrete classes derived from Base
found in class libraries (*.dll) in the application path and stores them in a list.
以下代码片段 (C#) 构造从Base
在应用程序路径中的类库 (*.dll) 中找到的任何具体类的实例,并将它们存储在列表中。
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);
}
}
}
Edit:The classes referred to by Mattare probably a better option in .NET 3.5.
编辑:Matt引用的类可能是 .NET 3.5 中更好的选择。
回答by Matt Hamilton
As of .NET 3.5 there's a formalized, baked-in way to create and load plugins from a .NET application. It's all in the System.AddInnamespace. For more information you can check out this article on MSDN: Add-ins and Extensibility
从 .NET 3.5 开始,有一种正式的、内置的方式可以从 .NET 应用程序创建和加载插件。这一切都在System.AddIn命名空间中。有关更多信息,您可以查看 MSDN 上的这篇文章:插件和可扩展性
回答by cdiggins
Dynamically Loading Plug-ins
动态加载插件
For information on how to dynamically load .NET assemblies see this question(and my answer). Here is some code for loading creating an AppDomain
and loading an assembly into it.
有关如何动态加载 .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();
Unloading Plug-ins
卸载插件
A typical requirement of a plugin framework is to unload the plugins. To unload dynamically loaded assemblies (e.g. plug-ins and add-ins) you have to unload the containing AppDomain
. For more information see this article on MSDN on Unloading AppDomains.
插件框架的一个典型要求是卸载插件。要卸载动态加载的程序集(例如插件和加载项),您必须卸载包含AppDomain
. 有关更多信息,请参阅MSDN 上关于卸载 AppDomains 的这篇文章。
Using WCF
使用 WCF
There is a stack overflow question and answerthat describe how to use the Windows Communication Framework (WCF) to create a plug-in framework.
有一个堆栈溢出问答,描述了如何使用 Windows 通信框架 (WCF) 创建插件框架。
Existing Plug-in Frameworks
现有插件框架
I know of two plug-in frameworks:
我知道两个插件框架:
- Mono.Add-ins- As mentioned in this answer to another question.
- Managed Add-in Framework (MAF)- This is the
System.AddIn
namespace as mentioned by Matt in his answer.
- Mono.Add-ins- 正如在这个对另一个问题的回答中提到的。
- 托管加载项框架 (MAF)- 这是Matt 在他的回答中提到的
System.AddIn
命名空间。
Some people talk about the Managed Extensibility Framework (MEF)as a plug-in or add-in framework, which it isn't. For more information see this StackOverflow.com questionand this StackOverflow.com question.
有些人将托管可扩展性框架 (MEF)称为插件或加载项框架,但事实并非如此。有关更多信息,请参阅此 StackOverflow.com 问题和此 StackOverflow.com 问题。
回答by Patrik Svensson
One tip is to load all plugins and such into an own AppDomain, since the code running can be potentially malicious. An own AppDomain can also be used to "filter" assemblies and types that you don't want to load.
一个技巧是将所有插件等加载到自己的 AppDomain 中,因为运行的代码可能是恶意的。自己的 AppDomain 也可用于“过滤”您不想加载的程序集和类型。
AppDomain domain = AppDomain.CreateDomain("tempDomain");
And to load an assembly into the application domain:
并将程序集加载到应用程序域中:
AssemblyName assemblyName = AssemblyName.GetAssemblyName(assemblyPath);
Assembly assembly = domain.Load(assemblyName);
To unload the application domain:
卸载应用程序域:
AppDomain.Unload(domain);
回答by Jason Olson
Yes, ++ to Matt and System.AddIn (a two-part MSDN magazine article about System.AddIn are available hereand here). Another technology you might want to look at to get an idea where the .NET Framework might be going in the future is the Managed Extensibility Frameworkcurrently available in CTP form on Codeplex.
是的,++ 到 Matt 和 System.AddIn(有关 System.AddIn 的两部分 MSDN 杂志文章可在此处和此处获得)。为了了解 .NET Framework 未来的发展方向,您可能想要查看的另一项技术是目前在 Codeplex 上以 CTP 形式提供的托管扩展性框架。
回答by Biri
Basically you can do it in two ways.
基本上你可以通过两种方式做到这一点。
The first is to import kernel32.dll and use LoadLibrary and GetProcAddress as you used it before:
首先是导入 kernel32.dll 并使用您之前使用的 LoadLibrary 和 GetProcAddress:
[DllImport("kernel32.dll")]
internal static extern IntPtr LoadLibrary(String dllname);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetProcAddress(IntPtr hModule, String procname);
The second is to do it in the .NET-way: by using reflection. Check System.Reflection namespace and the following methods:
第二种是以 .NET 方式实现的:通过使用反射。检查 System.Reflection 命名空间和以下方法:
First you load the assembly by it's path, then get the type (class) from it by it's name, then get the method of the class by it's name again and finally call the method with the relevant parameters.
首先通过它的路径加载程序集,然后通过它的名称从中获取类型(类),然后再次通过它的名称获取类的方法,最后使用相关参数调用该方法。
回答by Brian G Swanson
The article is a bit older, but still applicable for creating an extensibility layer within your application:
这篇文章有点旧,但仍然适用于在您的应用程序中创建扩展层:
Let Users Add Functionality to Your .NET Applications with Macros and Plug-Ins
回答by LeonardoX
This is my implementation, Inspired in this codeavoiding to iterate over all assemblies and all types (or at least filtering with linQ). I just load the library and try to load the class which implemets a common shared interface. Simple and fast :)
这是我的实现,受到这段代码的启发,避免迭代所有程序集和所有类型(或至少使用 linQ 过滤)。我只是加载库并尝试加载实现公共共享接口的类。简单快速:)
Just declare an interface in a separated library and reference it in both, your system and your plugin:
只需在单独的库中声明一个接口并在您的系统和插件中引用它:
public interface IYourInterface
{
Task YourMethod();
}
In your plugin library, declare a class which implements IYourInterface
在您的插件库中,声明一个实现 IYourInterface 的类
public class YourClass: IYourInterface
{
async Task IYourInterface.YourMethod()
{
//.....
}
}
In your system, declare this method
在您的系统中,声明此方法
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Linq;
public abstract class ReflectionTool<TSource> where TSource : class
{
public static TSource LoadInstanceFromLibrary(string libraryPath)
{
TSource pluginclass = null;
if (!System.IO.File.Exists(libraryPath))
throw new Exception($"Library '{libraryPath}' not found");
else
{
Assembly.LoadFrom(libraryPath);
var fileName = System.IO.Path.GetFileName(libraryPath).Replace(".dll", "");
var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(c => c.FullName.StartsWith(fileName));
var type = assembly.GetTypes().FirstOrDefault(c => c.GetInterface(typeof(TSource).FullName) != null);
try
{
pluginclass = Activator.CreateInstance(type) as TSource;
}
catch (Exception ex)
{
LogError("", ex);
throw;
}
}
return pluginclass;
}
}
And call it like this way:
并这样称呼它:
IYourInterface instance = ReflectionTool<IYourInterface>.LoadInstanceFromLibrary("c:\pathToYourLibrary.dll");