C# で DLL の 動的 呼び出し & 読み込み

0 件のコメント

今回は、指定フォルダ配下にある、指定I/Fを実装したアセンブリを、動的に読み込んでインスタンスを生成する実装を行います。

目次

  • ソリューション構成
  • 取り決めごとにする インターフェース の作成
  • プラグイン実態(dllファイル) の 実装
  • プラグイン を読み込んで インスタンス の生成

サンプルコード

ソリューション構成

プロジェクトは以下に示す3つを作ります。

  • MainWindow
    dll動的読み込みをテストする実行可能ファイル。WPFとかWindowsFormsApplicationとか…。IPlugin プロジェクト を参照します。
  • IPlugin
    取り決めごとにするインターフェース。クラスライブラリ。MainWindow と SamplePlugin から参照される。
  • SamplePlugin
    プラグインの実態。クラスライブラリ。IPlugin プロジェクト を参照します。

取り決めごとにする インターフェース の作成

IPlugin.cs

namespace Sample
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    public interface IPlugin
    {
        string Message();
    }
}

インターフェースは Message を返すだけの単純なものを設定します。 必要であればこの インターフェース を追加、修正していきます。

プラグイン実態(dllファイル) の 実装

SamplePlugin.cs

namespace Sample
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    public class Class1 : IPlugin
    {
        public string Message()
        {
            return "こんちわ";
        }
    }
}

インターフェース を実装しただけです。

プラグイン を読み込んで インスタンス の生成

MainWindow.xaml.cs

namespace Sample
{
    using System.IO;
    using System.Reflection;
    using System.Windows;

    public partial class MainWindow : Window
    {
        /// <summary>
        /// 実行ボタンを押下したとき呼び出されます。
        /// </summary>
        /// <param name="sender">イベント発生元</param>
        /// <param name="e">イベントオブジェクト</param>
        private void btnExecute_Click(object sender, RoutedEventArgs e)
        {
            var interfaceName = typeof(IPlugin).FullName;
            var typeName = string.Empty;
            IPlugin plugin = null;

            // プラグインファイルパス一覧の取得
            var pluginFilePathList = this.GetPluginFilePathList();

            // プラグインからインスタンス生成
            foreach (var pluginFilePath in pluginFilePathList)
            {
                plugin = this.CreateInstance<IPlugin>(pluginFilePath);
                if (plugin == null)
                {
                    continue;
                }
                this.textBox_filename.Text += plugin.Message();
            }
        }

        /// <summary>
        /// プラグインファイルパス一覧を取得します。
        /// </summary>
        /// <returns>プラグインへのファイルパス一覧</returns>
        private string[] GetPluginFilePathList()
        {
            var entryDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
            var pluginDirectory = @"plugin";
            var targetDirectory = Path.Combine(entryDirectory, pluginDirectory);

            if (!Directory.Exists(targetDirectory))
            {
                return new string[] { };
            }

            return Directory.GetFiles(targetDirectory, "*.dll", SearchOption.AllDirectories);
        }

        /// <summary>
        /// 指定された dll のインスタンスを生成します。
        /// </summary>
        /// <typeparam name="T">インターフェース クラス</typeparam>
        /// <param name="filepath">プラグイン ファイルパス</param>
        /// <returns>プラグイン から生成 されたインスタンス</returns>
        private T CreateInstance<T>(string filepath)
            where T : class
        {
            Assembly assembly = null;
            string typeName = string.Empty;
            string interfaceName = typeof(T).FullName;

            assembly = Assembly.LoadFrom(filepath);

            if (assembly == null)
            {
                return null;
            }

            foreach (var type in assembly.GetTypes())
            {
                if (type.IsClass && type.IsPublic && !type.IsAbstract && type.GetInterface(interfaceName) != null)
                {
                    typeName = type.FullName;
                    break;
                }
            }

            return assembly.CreateInstance(typeName) as T;
        }
    }
}

- btnExecute_Click(object, RoutedEventArgs) : void

エントリーポイント。 ボタンが押されたときに dll を読み込んでインスタンス生成を始めます。

- GetPluginFilePathList() : string[]

プラグイン dll ファイルパス一覧 を取得します。 実行可能ファイルから見て、"plugin" の相対位置にあるフォルダ配下に含まれる拡張子 "*.dll" を探し出します。 実行可能ファイルの場所は Assembly.GetEntryAssembly().Location で取得します。 指定フォルダ以下のファイル検索は Directory.GetFiles() で取得します。

- CreateInstance<T>(string) : T

プラグイン dll への絶対パスを引数にとって インスタンス を生成します。 指定されたインターフェースを実装したクラスのインスタンスを生成して返します。 アセンブリ は Assembly.LoadFrom() で読み込み、 assembly.CreateInstance() で インスタンス生成 します。 try-catch を行っていないので、適宜追加実装してください。。

おまけ

以下のような ビルド イベント を登録しておくと、コピーしなくて済むので便利です。 TARGET_PRJ の値は任意に書き換えてください。

set MODE=$(ConfigurationName)
set SOLUTION_DIR=$(SolutionDir)
set SOURCE_PRJ=$(ProjectName)
set TARGET_PRJ=Application
set PLUGIN_DIR=plugin

set SOURCE_DIR=%SOLUTION_DIR%%SOURCE_PRJ%\bin\%MODE%\*.dll
set TARGET_DIR=%SOLUTION_DIR%%TARGET_PRJ%\bin\%MODE%\%PLUGIN_DIR%

xcopy "%SOURCE_DIR%" "%TARGET_DIR%" /e /c /i /r /y /f

今回、以下のサイトを参考にしました。