Загрузка сборок как плагинов - PullRequest
1 голос
/ 11 августа 2009

У меня есть проект winforms, который позволяет будущим разработчикам создавать плагины для приложения. Приложение Winforms содержит все интерфейсы и классы, на которые разработчик будет ссылаться при создании плагина. Следовательно, когда я создаю плагин, я ссылаюсь на библиотеки основного приложения.

Когда основное приложение инициализируется, я создаю отдельный домен для загрузки всех библиотек плагина. Я делаю это потому, что могу использовать вызов appDomain.unload, чтобы удалить плагины по желанию, а затем перезагрузить остальные плагины. Когда я запускаю Debug в VS2008, мое приложение инициализируется, оно загружает первый плагин, но я получаю предупреждение, что мне нужно загрузить ссылочные dll плагина, которые я использую для ссылки на интерфейсы основного приложения.

Теперь мой вопрос перед загрузкой библиотек dll в поддомен. Могу ли я использовать интерфейсы основного приложения, чтобы создать их экземпляр для плагинов, которые можно использовать как ссылку? Если да, как я могу это сделать? Любая помощь приветствуется.

это PluginManager моего приложения, который загружает плагины, найденные в файле app.config. Я сохранил только метод, по которому мне нужна помощь, и конструктор класса. В этом классе я читаю файл app.cinfig и помещаю содержимое customConfigSection в коллекцию customConfig. Когда вызывается loadAssemblies, я перебираю коллекцию и добавляю сборки в поддомен, созданный в конструкторе.

using System;
using MyApp.Data;
using MyApp.Interfaces;
using MyApp.Variables;
using System.Reflection;

namespace MyApp.Core
{
    /// <summary>
    /// This object helps the application manage its scalable and extended components
    /// called plugins.
    /// </summary>
    public class PlugInManager
    {
        public PlugInManager()
        {

            //appDomain setup
            pluginDomainSetup = new AppDomainSetup();
            pluginDomainSetup.ApplicationBase = pluginDomainLocation;
            pluginDomainSetup.DisallowCodeDownload = true;
            string pluginApplicationName = string.Format(MAOIE.Variables.Constants.PLUGIN_APPLICATION_NAME);

            //appDomain creation
            pluginDomain = AppDomain.CreateDomain(pluginApplicationName, null, pluginDomainSetup);

            //Loads the values located in the config file
            LoadPluginConfiguration();
            //Load any existing plugins in the directories
            LoadAssemblies();
        }

        private void LoadAssemblies()
        {
            //I"m thinking I should add this the referenced libraries to the subdomain here.

            //AppDomain.Unload(this.pluginDomain);   
            string reference = GetReferencePath();
            reference += Variables.Constants.MAOIE_CORE_DLL;


            //Iterate through the items found in the app.config file.
            foreach (PluginSetting item in this.PluginConfigSettings.PluginItems)
            {
                string file = GetPluginPath();
                file += item.PluginFileName;

                switch (item.PluginType)
                {
                case Constants.PluginType.pluginTypeA:
                    pluginDomain.CreateInstanceFrom(file, item.PluginAssemblyType);

                    IPluginTypeA ia = (IPluginTypeA)Activator.CreateInstance(pluginDomain, item.PluginFileName, item.PluginAssemblyType);
                    Plugable<IPluginTypeA> pia = new Plugable<IPluginTypeA>();
                    pia.ConcreteClass = ia;
                    pia.Core = false;
                    //collection used throughout the application
                    this.aerodynamicAnalyzers.Add(pia);

                    return;
                case Constants.PluginType.pluginTypeB:
                    pluginDomain.CreateInstanceFrom(file, item.PluginAssemblyType);

                    IPluginTypeB ib = (IPluginTypeB)Activator.CreateInstance(pluginDomain, item.PluginFileName, item.PluginAssemblyType);
                    Plugable<IPluginTypeB> pib = new Plugable<IPluginTypeB>();
                    piB.ConcreteClass = ib;
                    pim.Core = false;
                    //collection used throughout the application
                    this.missionAnalyzers.Add(pib);
                    return;
                case Constants.PluginType.pluginTypeC:
                    pluginDomain.CreateInstanceFrom(file, item.PluginAssemblyType);

                    IPluginTypeC ic = (IPluginTypeC)Activator.CreateInstance(pluginDomain, item.PluginFileName, item.PluginAssemblyType);
                    Plugable<IPluginTypeC> pic = new Plugable<IPluginTypeC>();
                    pic.ConcreteClass = ic;
                    pic.Core = false;
                    //collection used throughout the application
                    this.pluginTypeCs.Add(pio);
                    return;
                case Constants.PluginType.pluginTypeD:
                    pluginDomain.CreateInstanceFrom(file, item.PluginAssemblyType);

                    IPluginTypeD id = (IPluginTypeD)Activator.CreateInstance(pluginDomain, item.PluginFileName, item.PluginAssemblyType);
                    Plugable<IPluginTypeD> piw = new Plugable<IPluginTypeD>();
                    pid.ConcreteClass = id;
                    pid.Core = false;
                    //collection used throughout the application
                    this.pluginTypeDs.Add(pid);
                    return;
                }
            }
        }
    }
    //end PlugInManager

}
//end namespace  MyApp.Core

Этот следующий класс является отдельным классом в отдельном проекте. Я вставляю пустой плагин для проверки метода LoadAssemblies моего pluginManager. Я добавляю ссылки на MyApp.Core.dll, который копируется Visual Studio в каталог bin этого проекта. Мне это нужно для реализации интерфейса, найденного в основном приложении.

Этот класс - просто методы получения и установки плюс пустой метод.

///////////////////////////////////////////////////////////
//  testPluginA.cs
// used as an empty class to test the import of plugins into the main application.
///////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Text;
using MyApp.Core;

namespace testPluginA
{
    public class testPluginA : MyApp.Interfaces.IPluginTypeA
    {
        public testPluginA()
        { 

        }

        private string name = "testPluginA";
        private string desc = "Test Plugin A 1";
        /// <summary>
        /// The description of the plugin.
        /// </summary>
        public string Description { get{return this.desc;} }
        /// <summary>
        /// The display name of the plugin.
        /// </summary>
        public string FriendlyName { get{return this.name;} }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="mp"></param>
        public void Optimize(MyApp.Data.Car car)
        { 
            //does nothing
        }

1 Ответ

2 голосов
/ 11 августа 2009

Загрузите плагины в один и тот же домен, но заставьте их реализовать интерфейс, который предоставляет методы Load и Unload. Публичный контракт метода Unload является [как минимум] следующим:

  • Dispose() где применимо и null все ссылки на ресурсы, используемые плагином, чтобы GC мог их собрать [в какой-то момент в будущем]
  • Удалите его «соединения» (независимо от их формы) в основное приложение, чтобы состояние основного приложения было таким же, как до загрузки плагина
  • Поместите плагин в состояние, где Load является допустимой операцией

Ответственность за выполнение этого контракта несут ваши авторы плагинов. В любом случае вы загружаете их код для выполнения - если они нарушают правила, вещи будут работать неправильно, независимо от того, поместите ли вы их в свой собственный домен приложений.

Редактировать: Лично я думаю, что Managed Extensibility Framework стоит по крайней мере , которую стоит изучить (и использовать, если она соответствует вашим потребностям), когда вы создаете свой API, хотя некоторые здесь могут не согласиться .

...