【问题标题】:How to load plugins in .NET?如何在 .NET 中加载插件?
【发布时间】:2008-08-18 07:34:56
【问题描述】:

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

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

【问题讨论】:

    标签: .net windows plugins add-in extensibility


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      以下代码 sn-p (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 中可能是更好的选择。

      【讨论】:

        【解决方案3】:

        动态加载插件

        有关如何动态加载 .NET 程序集的信息,请参阅 this question(和 my answer)。下面是一些用于加载创建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。如需更多信息,请参阅this article on MSDN on Unloading AppDomains

        使用 WCF

        有一个stack overflow question and answer 描述了如何使用 Windows 通信框架 (WCF) 创建插件框架。

        现有的插件框架

        我知道两个插件框架:

        有些人将Managed Extensibility Framework (MEF) 称为插件或插件框架,但事实并非如此。有关详细信息,请参阅 this StackOverflow.com questionthis StackOverflow.com question

        【讨论】:

          【解决方案4】:

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

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

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

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

          卸载应用程序域:

          AppDomain.Unload(domain);
          

          【讨论】:

            【解决方案5】:

            是的,对 Matt 和 System.AddIn 使用 ++(有关 System.AddIn 的两部分 MSDN 杂志文章可在 herehere 获得)。您可能希望了解 .NET Framework 未来发展方向的另一项技术是 Managed Extensibility Framework,目前在 Codeplex 上以 CTP 形式提供。

            【讨论】:

              【解决方案6】:

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

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

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

              【讨论】:

                【解决方案7】:

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

                Let Users Add Functionality to Your .NET Applications with Macros and Plug-Ins

                【讨论】:

                • 嗯,不幸的是,链接已经失效了,这并没有像你包含标题那样糟糕,但是我没有在快速谷歌中找到它:-(跨度>
                【解决方案8】:

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2010-09-06
                  • 2012-02-26
                  • 2012-01-30
                  • 2023-04-08
                  • 2023-04-07
                  • 1970-01-01
                  • 2012-12-29
                  • 1970-01-01
                  相关资源
                  最近更新 更多