27

我想提供一些在我的软件中创建动态可加载插件的方法。执行此操作的典型方法是使用LoadLibrary WinAPI 函数加载 dll 并调用GetProcAddress以获取指向该 dll 内函数的指针。

我的问题是如何在 C#/.Net 应用程序中动态加载插件?

4

8 回答 8

28

从 .NET 3.5 开始,有一种正式的、内置的方法可以从 .NET 应用程序创建和加载插件。这一切都在System.AddIn命名空间中。有关更多信息,您可以查看 MSDN 上的这篇文章:加载项和可扩展性

于 2008-08-18T08:07:21.047 回答
20

以下代码片段 (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);
        }
    }
}

编辑:Matt提到的类可能是 .NET 3.5 中更好的选择。

于 2008-08-18T07:41:24.390 回答
8

动态加载插件

有关如何动态加载 .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. 有关详细信息,请参阅MSDN 上有关卸载 AppDomains 的这篇文章

使用 WCF

有一个堆栈溢出问题和答案,描述了如何使用 Windows 通信框架 (WCF) 创建插件框架。

现有的插件框架

我知道两个插件框架:

有些人将托管可扩展性框架 (MEF)称为插件或插件框架,但事实并非如此。有关更多信息,请参阅此 StackOverflow.com 问题此 StackOverflow.com 问题

于 2013-01-06T18:56:30.023 回答
5

一个技巧是将所有插件等加载到自己的 AppDomain 中,因为运行的代码可能是恶意的。也可以使用自己的 AppDomain 来“过滤”您不想加载的程序集和类型。

AppDomain domain = AppDomain.CreateDomain("tempDomain");

并将程序集加载到应用程序域中:

AssemblyName assemblyName = AssemblyName.GetAssemblyName(assemblyPath);
Assembly assembly = domain.Load(assemblyName);

要卸载应用程序域:

AppDomain.Unload(domain);
于 2008-08-18T08:09:40.527 回答
4

是的,Matt 和 System.AddIn 的 ++(关于 System.AddIn 的两部分 MSDN 杂志文章可在此处此处获得)。为了了解 .NET Framework 未来的发展方向,您可能希望了解的另一项技术是Codeplex 目前以 CTP 形式提供的托管可扩展性框架。

于 2008-09-09T22:03:07.717 回答
3

基本上你可以通过两种方式做到这一点。

第一个是导入 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);

第二种是以 .NET 方式进行:通过使用反射。检查 System.Reflection 命名空间和以下方法:

首先通过它的路径加载程序集,然后通过它的名称从中获取类型(类),然后再次通过它的名称获取类的方法,最后使用相关参数调用该方法。

于 2008-08-18T07:44:55.757 回答
2

这篇文章有点老,但仍然适用于在您的应用程序中创建可扩展层:

让用户使用宏和插件向您的 .NET 应用程序添加功能

于 2008-08-18T18:32:17.807 回答
0

这是我的实现,受此代码启发,避免迭代所有程序集和所有类型(或至少使用 linQ 过滤)。我只是加载库并尝试加载实现公共共享接口的类。简单快速:)

只需在单独的库中声明一个接口并在您的系统和插件中引用它:

public interface IYourInterface
{
    Task YourMethod();
}

在您的插件库中,声明一个实现 IYourInterface 的类

public class YourClass: IYourInterface
{
    async Task IYourInterface.YourMethod()
    {
        //.....
    }
}

在您的系统中,声明此方法

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;
    }
}

并这样称呼它:

IYourInterface instance = ReflectionTool<IYourInterface>.LoadInstanceFromLibrary("c:\pathToYourLibrary.dll");
于 2020-01-02T23:17:34.590 回答