プログラムの実行中にDLLを読み込む方法を紹介します。
例えばサーバーからDLLをダウンロードして、置き換えをするプログラムを作成したとします。この時プログラムがDLLを参照していると、ファイルが読み込まれていて置き換えることができません。
なのでダウンロード後にDLLを読み込ませる必要があります。そこで今回紹介する動的にDLLを読み込むなどが役に立っていきます。
DLLを読み込む方法
以下がDLLを動的に読み込む最低限の方法です。
//ファイル読み込み
var asm = System.Reflection.Assembly.LoadFrom('DLLのファイルパス');
//すべてのクラス型を取得する
foreach (Type type in asm.GetTypes())
{
//対象のクラスのインスタンスを作成
dynamic dll_object = Activator.CreateInstance(type);
}
dll_object でインスタンスを作っているのでそのクラスが持っているプロパティやクラスが使えます。
動的に読み込むためのDLLを準備する(プラグイン機能の実装)
読込む対象のDLLを自分で作る場合、お勧めしたいのがDLLを読み込む上でベースとなるクラスを継承することです。
動的にDLLを読み込む際、daynamicでオブジェクトを取得するため、クラスが本当に使いたい関数を持っているかわかりませんそこでインターフェースや仮想クラスなどを継承することで、その関数を必ず持っているということを担保します。
動的DLLのベースになるインターフェースを作る
BaseになるインターフェースDLLをPlugInBase.dllという名前で仮に作成してみます。
以下がソース例です。
※あくまでここで出てくる名前空間やインターフェース名や変数名や関数名は例です。
namespace PlugInBase
{
public interface IDynamicLoad
{
string PluginName { set; get; } //プラグイン名称
void Init(); //初期化処理
void Show(); //表示処理
void Output(string text); //出力処理
void ClickEvent(object button); //クリックイベント
}
}
プラグインを作成
インターフェースを継承したクラスを作成します。このDLLが実際に動的に読み込むDLLです。
先ほど説明したベースとなるクラスを継承したいので「参照に追加」します。

参照をクリックし、ベースとなるプラグインファイルを読み込みます。

以下にインターフェースを継承したクラスの例を作成しました。ビルドして出力します。
using System;
using PlugInBase;
using System.Windows.Forms;
namespace DynamicClassLibrary
{
public class DynamicLoadClass : IDynamicLoad
{
public string PluginName { get; set; } = "動的に読込まれたDLL";
public void ClickEvent(object control)
{
if ((control is Button) == false) return;
Button button = control as Button;
button.Click += Buuton_Click;
}
public void Init()
{
Console.WriteLine("Init");
}
public void Output(string text)
{
Console.WriteLine($"Output:{text}");
}
public void Show()
{
MessageBox.Show("Show");
}
private void Buuton_Click(object sender, EventArgs e)
{
//ボタンクリックイベントを取得できたのでFormを呼び出すなり、処理をするなりできる
MessageBox.Show("クリックイベントが動作しました。");
}
}
}
呼び出しもともベースのインターフェースを読み込んでおく
実際に動的に呼ぶほうのプロジェクトもインターフェースDLLを参照しておきます。
理由は、対象のインターフェースを継承していないクラスは読込たくないので比較に利用します。
DLLを動的に読み込む
こんな画面を用意しました。

DLLを読み込むと読み込んだ数でボタンが追加されるようなっています。DLLをEXEの直下フォルダに集めました。

実際のソースです。
private void btnDLLDynamicLoad_Click(object sender, EventArgs e)
{
//EXEの場所からDLLのディレクトリを取得する
string dll_dir = System.Windows.Forms.Application.StartupPath + @"\DLL";
//EXEの場所にあるDLLファイルをすべて読み込む
foreach (string dll in System.IO.Directory.GetFiles(dll_dir, "*.dll"))
{
//ファイル読み込み
var asm = System.Reflection.Assembly.LoadFrom(dll);
//DLLの中のTypeをすべて取得し、プラグインのタイプがあるかチェックする
foreach (Type type in asm.GetTypes())
{
//プラグインかどうかは継承元がプラグインベースのインターフェースを継承しているか
if (type is PlugInBase.IDynamicLoad) continue;
//インターフェースはインスタンスを作成しない。
if (type.IsInterface == true) continue;
//対象のクラスのインスタンスを作成
dynamic plugin = Activator.CreateInstance(type);
//ボタンを作成
Button button = new Button();
button.AutoSize = true;
//BasePluginClassを継承していれば必ずPluginNameがある
button.Text = plugin.PluginName;
button.Tag = plugin;
//ボタンのクリックイベントをプラグインに渡す。
plugin.ClickEvent(button);
//作成したボタンをフローレイアウトパネルに渡す
this.flowLayoutPanel1.Controls.Add(button);
}
}
}
簡単に言えばIDynamicLoadを継承しているクラスのみインスタンスを作成し、作成したインスタンスでボタンを作成するプログラムです。

ボタンを押すとメッセージダイアログがでます。

ボタンを押したときの処理を細かくしていけば結構なんでもできると思います。
まとめ
動的にDLLを読み込む利点は、DLLの場所を任意に置けるところだと思います。そのおかげで例えばサーバーに最新DLLを置いておいてクライアントでDLLの更新日や製品バージョンをチェックし、変更があればダウンロードして置き換えることで、自動で更新が行われるシステムになります。
注意点としてはプラグインのベースとなるインターフェースをむやみに変えないことです。プラグイン側のインターフェースと読み込む側のインターフェースに差異が出てしまうと比較で失敗する可能性があるので注意です。
インタフェースクラスを変更する際は、プラグイン側と読み込む側両方ビルドしてください。

コメント