编写 C# 插件系统

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1070787/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:32:33  来源:igfitidea点击:

Writing C# Plugin System

c#pluginsextensibility

提问by Zack

I'm trying to write a plugin system to provide some extensibility to an application of mine so someone can write a plugin(s) for the application without touching the main application's code (and risk breaking something).

我正在尝试编写一个插件系统来为我的应用程序提供一些可扩展性,以便有人可以为该应用程序编写一个或多个插件,而无需触及主应用程序的代码(并且有破坏某些东西的风险)。

I've got the base "IPlugin" interface written (atm, nothing is implemented yet)

我已经编写了基本的“IPlugin”接口(atm,还没有实现)

Here is how I'm loading:

这是我的加载方式:

public static void Load()
{
    // rawr: http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx
    String[] pluginFiles = Directory.GetFiles(Plugins.PluginsDirectory, "*.dll");
    foreach (var plugin in pluginFiles)
    {
        Type objType = null;
        try
        {
            //Assembly.GetExecutingAssembly().GetName().Name
            MessageBox.Show(Directory.GetCurrentDirectory());
            Assembly asm = Assembly.Load(plugin);
            if (asm != null)
            {
                objType = asm.GetType(asm.FullName);
                if (objType != null)
                {
                    if (typeof(IPlugin).IsAssignableFrom(objType))
                    {
                        MessageBox.Show(Directory.GetCurrentDirectory());
                        IPlugin ipi = (IPlugin)Activator.CreateInstance(objType);
                        ipi.Host = Plugins.m_PluginsHost;
                        ipi.Assembly = asm;
                    }
                }
            }
        }
        catch (Exception e)
        {
            MessageBox.Show(e.ToString(), "Unhandled Exception! (Please Report!)", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information);
        }
    }
}

A friend tried to help but I really didn't understand what was wrong.

一位朋友试图提供帮助,但我真的不明白出了什么问题。

The folder structure for plugins is the following:

插件的文件夹结构如下:

\
\Plugins\

\
\插件\

All plugins reference a .dll called "Lab.Core.dll" in the [root] directory and it is not present in the Plugins directory because of duplicate references being loaded.

所有插件都在 [root] 目录中引用了一个名为“Lab.Core.dll”的 .dll,并且由于加载了重复引用,它不存在于 Plugins 目录中。

The plugin system is loaded from Lab.Core.dll which is also referenced by my executable. Type "IPlugin" is in Lab.Core.dll as well. Lab.Core.dll is, exactly as named, the core of my application.

插件系统从 Lab.Core.dll 加载,我的可执行文件也引用了它。类型“IPlugin”也在 Lab.Core.dll 中。Lab.Core.dll 正是我的应用程序的核心。

EDIT:

编辑:

Question: Why/What is that exception I'm getting and how could I go about fixing it?

问题:为什么/我遇到的异常是什么,我该如何修复它?

FINAL EDIT:

最终编辑:

Ok so I decided to re-write it after looking at some source code a friend wrote for a TF2 regulator.

好的,所以在查看了朋友为 TF2 调节器编写的一些源代码后,我决定重新编写它。

Here's what I got and it works:

这是我得到的并且有效:

    public class TestPlugin : IPlugin {
    #region Constructor

    public TestPlugin() {
        //
    }

    #endregion

    #region IPlugin Members

    public String Name {
        get {
            return "Test Plugin";
        }
    }

    public String Version {
        get {
            return "1.0.0";
        }
    }

    public String Author {
        get {
            return "Zack";
        }
    }

    public Boolean OnLoad() {
        MessageBox.Show("Loaded!");
        return true;
    }

    public Boolean OnAllLoaded() {
        MessageBox.Show("All loaded!");
        return true;
    }

    #endregion
}

        public static void Load(String file) {
        if (!File.Exists(file) || !file.EndsWith(".dll", true, null))
            return;

        Assembly asm = null;

        try {
            asm = Assembly.LoadFile(file);
        } catch (Exception) {
            // unable to load
            return;
        }

        Type pluginInfo = null;
        try {
            Type[] types = asm.GetTypes();
            Assembly core = AppDomain.CurrentDomain.GetAssemblies().Single(x => x.GetName().Name.Equals("Lab.Core"));
            Type type = core.GetType("Lab.Core.IPlugin");
            foreach (var t in types)
                if (type.IsAssignableFrom((Type)t)) {
                    pluginInfo = t;
                    break;
                }

            if (pluginInfo != null) {
                Object o = Activator.CreateInstance(pluginInfo);
                IPlugin plugin = (IPlugin)o;
                Plugins.Register(plugin);
            }
        } catch (Exception) {
        }
    }

    public static void LoadAll() {
        String[] files = Directory.GetFiles("./Plugins/", "*.dll");
        foreach (var s in files)
            Load(Path.Combine(Environment.CurrentDirectory, s));

        for (Int32 i = 0; i < Plugins.List.Count; ++i) {
            IPlugin p = Plugins.List.ElementAt(i);
            try {
                if (!p.OnAllLoaded()) {
                    Plugins.List.RemoveAt(i);
                    --i;
                }
            } catch (Exception) {
                Plugins.List.RemoveAt(i);
                --i;
            }
        }
    }

采纳答案by Brad Barker

It sounds like you have a circular reference. You said your plugins reference Lab.Core.DLL, but you also say the plugins are loaded from Lab.Core.DLL.

听起来你有一个循环引用。你说你的插件引用了 Lab.Core.DLL,但你也说插件是从 Lab.Core.DLL 加载的。

Am I misunderstanding what is happening here?

我误解了这里发生的事情吗?

EDIT: OK now that you have added your question to the question...

编辑:好的,现在您已将问题添加到问题中...

You need to have Lab.Core.DLL accessible to the plugin being loaded since it is a dependency. Normally that would mean having it in the same directory or in the GAC.

您需要让正在加载的插件访问 Lab.Core.DLL,因为它是一个依赖项。通常这意味着将它放在同一目录或 GAC 中。

I suspect there are deeper design issues at play here, but this is your immediate problem.

我怀疑这里有更深层次的设计问题在起作用,但这是您的直接问题。

回答by Eric Lippert

The Managed Extensibility Framework (MEF) is a new library in .NET that enables greater reuse of applications and components. Using MEF, .NET applications can make the shift from being statically compiled to dynamically composed. If you are building extensible applications, extensible frameworks and application extensions, then MEF is for you.

托管可扩展性框架 (MEF) 是 .NET 中的一个新库,它支持更好地重用应用程序和组件。使用 MEF,.NET 应用程序可以从静态编译转变为动态组合。如果您正在构建可扩展的应用程序、可扩展的框架和应用程序扩展,那么 MEF 适合您。

http://www.codeplex.com/MEF

http://www.codeplex.com/MEF

Edit: CodePlex is going away - the code has been moved to Github for archival purposes only: https://github.com/MicrosoftArchive/mef

编辑:CodePlex 即将消失 - 代码已移至 Github 仅用于存档目的:https: //github.com/MicrosoftArchive/mef

回答by Jhonny D. Cano -Leftware-

As a side answer, i use these 2 interfaces for implementing that

作为附带答案,我使用这两个接口来实现

    public interface IPlugin {
        string Name { get; }
        string Description { get; }
        string Author { get; }
        string Version { get; }

        IPluginHost Host { get; set; }

        void Init();
        void Unload();

        IDictionary<int, string> GetOptions();
        void ExecuteOption(int option);
}

    public interface IPluginHost {
        IDictionary<string, object> Variables { get; }
        void Register(IPlugin plugin);
     }