web-dev-qa-db-ja.com

C#プラグインシステムの作成

私は私のアプリケーションに拡張性を提供するプラグインシステムを作成して、誰かがメインアプリケーションのコードに触れずに(そして何かを壊す危険を冒して)アプリケーションのプラグインを作成できるようにしています。

基本的な「IPlugin」インターフェースを記述しました(atm、まだ何も実装されていません)

これが私がロードしている方法です:

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

友人が助けようとしたが、何が悪いのか本当に理解できなかった。

プラグインのフォルダ構造は次のとおりです。

\
\プラグイン\

すべてのプラグインは[ルート]ディレクトリの「Lab.Core.dll」と呼ばれる.dllを参照しますが、ロードされている参照が重複しているため、プラグインディレクトリには存在しません。

プラグインシステムは、私の実行可能ファイルでも参照されるLab.Core.dllからロードされます。タイプ「IPlugin」もLab.Core.dllにあります。 Lab.Core.dllは、名前のとおり、アプリケーションのコアです。

編集:

質問:なぜ/私が受けているその例外は何ですか?どうすれば修正できますか?

最終編集:

わかりましたので、友人がTF2レギュレーターのために書いたいくつかのソースコードを見て、それを書き直すことにしました。

これが私が得たものであり、それは機能します:

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

循環参照があるようですね。プラグインはLab.Core.DLLを参照していると言いましたが、プラグインはLab.Core.DLLから読み込まれるとも言っています。

ここで何が起こっているのか誤解していますか?

編集:質問に質問を追加したので、OKです...

依存関係であるため、ロードされているプラ​​グインにアクセスできるLab.Core.DLLが必要です。通常、それは同じディレクトリまたはGACにあることを意味します。

ここには、より深い設計上の問題があると思いますが、これはあなたの差し迫った問題です。

7
Brad Barker

Managed Extensibility Framework(MEF)は.NETの新しいライブラリで、アプリケーションとコンポーネントの再利用を促進します。 MEFを使用すると、.NETアプリケーションは静的にコンパイルされたものから動的に構成されたものに移行できます。拡張可能なアプリケーション、拡張可能なフレームワーク、アプリケーション拡張機能を構築している場合、MEFが最適です。

http://www.codeplex.com/MEF

編集:CodePlexは廃止されます-コードはアーカイブ目的でのみGithubに移動されました: https://github.com/MicrosoftArchive/mef

28
Eric Lippert

副次的な回答として、私はそれを実装するためにこれらの2つのインターフェイスを使用します

///<summary>
///</summary>
public interface IPlugin {
    ///<summary>
    ///</summary>
    string Name { get; }
    ///<summary>
    ///</summary>
    string Description { get; }
    ///<summary>
    ///</summary>
    string Author { get; }
    ///<summary>
    ///</summary>
    string Version { get; }

    ///<summary>
    ///</summary>
    IPluginHost Host { get; set; }

    ///<summary>
    ///</summary>
    void Init();
    ///<summary>
    ///</summary>
    void Unload();

    ///<summary>
    ///</summary>
    ///<returns></returns>
    IDictionary<int, string> GetOptions();
    ///<summary>
    ///</summary>
    ///<param name="opcion"></param>
    void ExecuteOption(int option);

}



///<summary>
///</summary>
public interface IPluginHost {
    ///<summary>
    ///</summary>
    IDictionary<string, object> Variables { get; }
    ///<summary>
    ///</summary>
    ///<param name="plugin"></param>
    void Register(IPlugin plugin);
}