如何在.NET中加载插件?

发布于 2024-07-04 06:25:48 字数 332 浏览 11 评论 0 原文

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

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

I'd like to provide some way of creating dynamically loadable plugins in my software.
Typical way to do this is using the LoadLibrary WinAPI function to load a dll and calling GetProcAddress to get an pointer to a function inside that dll.

My question is how do I dynamically load a plugin in C#/.Net application?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

染年凉城似染瑾 2024-07-11 06:25:48

动态加载插件

有关如何动态加载 .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 上这篇有关卸载 AppDomain 的文章

使用 WCF

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

现有插件框架

我知道两个插件框架:

有些人将托管扩展性框架 (MEF) 视为插件-in 或附加框架,但它不是。 有关详细信息,请参阅此 StackOverflow.com 问题此 StackOverflow.com 问题

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.

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.

Using WCF

There is a stack overflow question and answer that describe how to use the Windows Communication Framework (WCF) to create a plug-in framework.

Existing Plug-in Frameworks

I know of two plug-in frameworks:

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 question and this StackOverflow.com question.

画▽骨i 2024-07-11 06:25:48

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

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

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

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

卸载应用程序域:

AppDomain.Unload(domain);

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 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);
淡紫姑娘! 2024-07-11 06:25:48

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

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

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.

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 Matt are probably a better option in .NET 3.5.

瞳孔里扚悲伤 2024-07-11 06:25:48

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

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.AddIn namespace. For more information you can check out this article on MSDN: Add-ins and Extensibility

不必了 2024-07-11 06:25:48

这是我的实现,灵感来自 此代码避免迭代所有程序集和所有类型(或至少使用 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");

This is my implementation, Inspired in this code avoiding 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 :)

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

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");
囍笑 2024-07-11 06:25:48

该文章有点旧,但仍然适用于在应用程序中创建可扩展性层:

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

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

不寐倦长更 2024-07-11 06:25:48

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

第一种方法是导入 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 命名空间和以下方法:

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

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:

[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:

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.

油饼 2024-07-11 06:25:48

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

Yes, ++ to Matt and System.AddIn (a two-part MSDN magazine article about System.AddIn are available here and 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 Framework currently available in CTP form on Codeplex.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文