#title(.NETプログラミング研究 第40号) #contents *.NETプログラミング研究 第40号 [#ucb8d008] **.NET Tips [#k1b62165] **プラグイン機能を持つアプリケーションを作成する - その2 [#u9b56728] #column(注意){{ この記事の最新版は「[[プラグイン機能を持つアプリケーションを作成する>http://dobon.net/vb/dotnet/programing/plugin.html]]」で公開しています。 }} (お詫び)今回はコードがかなり長くなってしまいました。ご了承ください。 前回の「プラグイン機能を持つアプリケーションを作成する - その1」ではプラグイン機能を実現させるための基本的な考え方について説明しました。簡単に復習すると、その方法とは、インターフェイスを使用するというものでした。詳細は前回のメールマガジンでご確認ください。 -[[.NETプログラミング研究 第39号>../39]] 今回はさらに実用的な例として、Windowsアプリケーションでプラグイン機能を実現する方法を考えます。ここでは具体的に、プラグインの使用できる簡単なエディタを作成します。 あくまでプラグイン機能を説明することが目的ですので、エディタは思い切り単純にし、フォームにRichTextBoxとMainMenuのみを配置することにします。プラグインからエディタのRichTextBoxコントロールにアクセスすることにより、プラグインの機能が果たせるようにします。 ***インターフェイスを定義する [#m998a78d] 前回と同じように、まずプラグインのクラスが実装すべきインターフェイスを定義します。今回はプラグインのインターフェイスに加えて、プラグインを使用するホストの側が実装すべきインターフェイスも定義することにします。ホストのためのインターフェイスでは、プラグインのホストとして必要な機能をメンバとして定義し、プラグインからこのメンバを通してホストにアクセスできるようにします。 具体的には、プラグインから指定されたメッセージをホストで表示するためのメソッドと、ホストのRichTextBoxコントロールを取得するためのプロパティ、さらにホストのメインフォームを取得するためのプロパティを定義することにします。 それでは実際にこれらのインターフェイスを作成してみましょう。前号と同様、クラスライブラリとして作成するため、Visual Studio .NETではクラスライブラリのプロジェクトを作成し("Plugin"という名前で作成しています)、.NET SDKでは/target:libraryコンパイラオプションを使用します。また、アセンブリファイル名は、"Plugin.dll"とします。さらに今回はWindowsアプリケーションを扱うため、"System.Windows.Forms.dll"を参照に追加します。(参照に追加するには、Visual Studio .NETの場合は、ソリューションエクスプローラの「参照設定」を、.NET SDKの場合は、/referenceコンパイラオプションを使用します。) コードは、次のようになります。IPluginインターフェイスがプラグインのためのインターフェイスで、IPluginHostインターフェイスがプラグインのホストのためのインターフェイスです。 #code(vbnet){{ Imports System Imports System.Windows.Forms Namespace Plugin ''' <summary> ''' プラグインで実装するインターフェイス ''' </summary> Public Interface IPlugin ''' <summary> ''' プラグインの名前 ''' </summary> ReadOnly Property Name() As String ''' <summary> ''' プラグインのバージョン ''' </summary> ReadOnly Property Version() As String ''' <summary> ''' プラグインの説明 ''' </summary> ReadOnly Property Description() As String ''' <summary> ''' プラグインのホスト ''' </summary> Property Host() As IPluginHost ''' <summary> ''' プラグインを実行する ''' </summary> Sub Run() End Interface ''' <summary> ''' プラグインのホストで実装するインターフェイス ''' </summary> Public Interface IPluginHost ''' <summary> ''' ホストのメインフォーム ''' </summary> ReadOnly Property MainForm() As Form ''' <summary> ''' ホストのRichTextBoxコントロール ''' </summary> ReadOnly Property RichTextBox() As RichTextBox ''' <summary> ''' ホストでメッセージを表示する ''' </summary> ''' <param name="plugin">メソッドを呼び出すプラグイン</param> ''' <param name="msg">表示するメッセージ</param> Sub ShowMessage(ByVal plugin As IPlugin, ByVal msg As String) End Interface End Namespace }} #code(csharp){{ using System; using System.Windows.Forms; namespace Plugin { /// <summary> /// プラグインで実装するインターフェイス /// </summary> public interface IPlugin { /// <summary> /// プラグインの名前 /// </summary> string Name {get;} /// <summary> /// プラグインのバージョン /// </summary> string Version {get;} /// <summary> /// プラグインの説明 /// </summary> string Description {get;} /// <summary> /// プラグインのホスト /// </summary> IPluginHost Host {get; set;} /// <summary> /// プラグインを実行する /// </summary> void Run(); } /// <summary> /// プラグインのホストで実装するインターフェイス /// </summary> public interface IPluginHost { /// <summary> /// ホストのメインフォーム /// </summary> Form MainForm {get;} /// <summary> /// ホストのRichTextBoxコントロール /// </summary> RichTextBox RichTextBox {get;} /// <summary> /// ホストでメッセージを表示する /// </summary> /// <param name="plugin">メソッドを呼び出すプラグイン</param> /// <param name="msg">表示するメッセージ</param> void ShowMessage(IPlugin plugin, string msg); } } }} IPluginは前回と比べ、プラグインのバージョンと説明を取得するためのプロパティが新たに追加され、さらに、Runメソッドもパラメータ、返り値がなくなりました。また、IPluginHostを設定、取得するためのプロパティも加えられています。 ***プラグインを作成する [#kd83490b] 次に、IPluginインターフェイスを実装したプラグインのクラスを作成します。ここでは、RichTextBox内の文字数を表示するプラグイン(CountCharsクラス)を作成してみましょう。 プラグインも前号と同様に、クラスライブラリとして作成し、"Plugin.dll"を参照に追加します。また、"System.Windows.Forms.dll"も参照に追加してください。出力するアセンブリファイル名は、"CountChars.dll"とします。(Visual Studio .NETのVB.NETの場合は、プロジェクトのプロパティの「ルート名前空間」が空白になっているものとします。デフォルトではプロジェクト名となっていますので、変更する必要があります。) CountCharsクラスのコードは次のようになります。 #code(vbnet){{ Imports System Namespace CountChars ''' <summary> ''' 文字数を表示するためのプラグイン ''' </summary> Public Class CountChars Implements Plugin.IPlugin Private _host As Plugin.IPluginHost 'IPluginのメンバ Public ReadOnly Property Name() As String _ Implements Plugin.IPlugin.Name Get Return "文字数取得" End Get End Property Public ReadOnly Property Version() As String _ Implements Plugin.IPlugin.Version Get '自分自身のAssemblyを取得し、バージョンを返す Dim asm As System.Reflection.Assembly = _ System.Reflection.Assembly.GetExecutingAssembly() Dim ver As System.Version = asm.GetName().Version Return ver.ToString() End Get End Property Public ReadOnly Property Description() As String _ Implements Plugin.IPlugin.Description Get Return "エディタで編集中の文章の文字数を表示します。" End Get End Property Public Property Host() As Plugin.IPluginHost _ Implements Plugin.IPlugin.Host Get Return Me._host End Get Set(ByVal Value As Plugin.IPluginHost) Me._host = Value End Set End Property ''' <summary> ''' RichTextBoxの文字数を表示する ''' </summary> Public Sub Run() Implements Plugin.IPlugin.Run Dim msg As String = String.Format("文字数 : {0} 文字", _ Me._host.RichTextBox.Text.Length) Me._host.ShowMessage(Me, msg) End Sub End Class End Namespace }} #code(csharp){{ using System; namespace CountChars { /// <summary> /// 文字数を表示するためのプラグイン /// </summary> public class CountChars : Plugin.IPlugin { private Plugin.IPluginHost _host; //IPluginのメンバ public string Name { get { return "文字数取得"; } } public string Version { get { //自分自身のAssemblyを取得し、バージョンを返す System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); System.Version ver = asm.GetName().Version; return ver.ToString(); } } public string Description { get { return "エディタで編集中の文章の文字数を表示します。"; } } public Plugin.IPluginHost Host { get { return this._host; } set { this._host = value; } } /// <summary> /// RichTextBoxの文字数を表示する /// </summary> public void Run() { string msg = string.Format("文字数 : {0} 文字", this._host.RichTextBox.Text.Length); this._host.ShowMessage(this, msg); } } } }} 特に説明を必要とする箇所はないでしょう。Runメソッドでは、IPluginHostオブジェクトからRichTextBoxにアクセスし、文字数を取得し、IPluginHostのShowMessageメソッドで結果をホストで表示しています。 ***ウィンドウを表示するプラグインの作成 [#r6228f91] 次にWindowsアプリケーションらしく、ウィンドウを表示するプラグインを作成してみましょう。ここでは、「検索」ウィンドウにより、RichTextBoxから指定された文字列を検索するプラグインを作ります(エディタとしては、プラグインで処理する機能ではありませんが)。 まず、CountCharsクラスと同様、クラスライブラリのプロジェクトを作成し(名前は、"FindString"とします)、"Plugin.dll"と"System.Windows.Forms.dll"を参照に追加します("System.Windows.Forms.dll"は今追加しなくても、「Windowsフォームの追加」でプロジェクトにフォームを追加すれば、自動的に追加されます)。 続いて、「Windowsフォームの追加」でプロジェクトにフォーム(FindForm)を追加し、「検索」ウィンドウを作成します。このフォームに配置するコントロール及び、変更するプロパティ(あるいはイベント)の一覧は次のようになります。 #pre{{ コントロール: Form Name: FindForm Text: 検索 AcceptButton: findButton CancelButton: closeButton FormBorderStyle: FixedToolWindow ShowInTaskbar: false コントロール: TextBox Name: findString Text: "" コントロール: Button Name: findButton Text: 次を検索 DialogResult: OK Clickイベント: findButton_Click コントロール: Button Name: closeButton Text: 閉じる DialogResult: Cancel Clickイベント: closeButton_Click コントロール: CheckBox Name: wholeWord Text: 単語単位で検索する コントロール: CheckBox Name: matchCase Text: 大文字小文字を区別する コントロール: CheckBox Name: reverse Text: 上へ検索する }} さらに、FindFormクラスに次のコードを追加し、指定されたRichTextBoxを検索できるようにします。 #code(vbnet){{ 'Imports System.Windows.Forms 'がコードの先頭に書かれているものとする '検索するRichTextBox Friend RichTextBox As RichTextBox Private Sub findButton_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles findButton.Click '検索オプションを決定する Dim finds As RichTextBoxFinds = RichTextBoxFinds.None If Me.wholeWord.Checked Then finds = finds Or RichTextBoxFinds.WholeWord End If If Me.matchCase.Checked Then finds = finds Or RichTextBoxFinds.MatchCase End If If Me.reverse.Checked Then finds = finds Or RichTextBoxFinds.Reverse End If '検索範囲を決定する Dim startPos, endPos As Integer If Not Me.reverse.Checked Then startPos = Me.RichTextBox.SelectionStart + _ Me.RichTextBox.SelectionLength endPos = -1 If startPos >= Me.RichTextBox.TextLength Then MessageBox.Show("検索が完了しました。", "検索") Return End If Else startPos = 0 endPos = Me.RichTextBox.SelectionStart If endPos <= 0 Then MessageBox.Show("検索が完了しました。", "検索") Return End If End If '検索する If Me.RichTextBox.Find( _ findString.Text, startPos, endPos, finds) < 0 Then MessageBox.Show("検索が完了しました。", "検索") Else Me.RichTextBox.Focus() End If End Sub Private Sub closeButton_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles closeButton.Click Me.Close() End Sub }} #code(csharp){{ //using System.Windows.Forms; //がコードの先頭に書かれているものとする //検索するRichTextBox internal RichTextBox RichTextBox; //「次を検索」がクリックされた時 private void findButton_Click( object sender, System.EventArgs e) { //検索オプションを決定する RichTextBoxFinds finds = RichTextBoxFinds.None; if (this.wholeWord.Checked) finds |= RichTextBoxFinds.WholeWord; if (this.matchCase.Checked) finds |= RichTextBoxFinds.MatchCase; if (this.reverse.Checked) finds |= RichTextBoxFinds.Reverse; //検索範囲を決定する int startPos, endPos; if (!this.reverse.Checked) { startPos = this.RichTextBox.SelectionStart + this.RichTextBox.SelectionLength; endPos = -1; if (startPos >= this.RichTextBox.TextLength) { MessageBox.Show("検索が完了しました。", "検索"); return; } } else { startPos = 0; endPos = this.RichTextBox.SelectionStart; if (endPos <= 0) { MessageBox.Show("検索が完了しました。", "検索"); return; } } //検索する if (this.RichTextBox.Find( findString.Text, startPos, endPos, finds) < 0) MessageBox.Show("検索が完了しました。", "検索"); else this.RichTextBox.Focus(); } //「閉じる」がクリックされた時 private void closeButton_Click( object sender, System.EventArgs e) { this.Close(); } }} 次にこのフォームを表示させるプラグインクラス(FindString)を作成します。コードは、次のようになります。 #code(vbnet){{ Imports System Imports System.Windows.Forms Namespace FindString Public Class FindString Implements Plugin.IPlugin Private _host As Plugin.IPluginHost Private _mainForm As FindForm 'IPluginのメンバ Public ReadOnly Property Name() As String _ Implements Plugin.IPlugin.Name Get Return "文字列の検索" End Get End Property Public ReadOnly Property Description() As String _ Implements Plugin.IPlugin.Description Get Return "編集中の文章から指定された文字列を検索します。" End Get End Property Public Property Host() As Plugin.IPluginHost _ Implements Plugin.IPlugin.Host Get Return _host End Get Set(ByVal Value As Plugin.IPluginHost) _host = Value End Set End Property Public ReadOnly Property Version() As String _ Implements Plugin.IPlugin.Version Get '自分自身のAssemblyを取得し、バージョンを返す Dim asm As System.Reflection.Assembly = _ System.Reflection.Assembly.GetExecutingAssembly() Dim ver As System.Version = asm.GetName().Version Return ver.ToString() End Get End Property Public Sub Run() Implements Plugin.IPlugin.Run 'フォームが表示されていれば、アクティブにして終了 If Not (Me._mainForm Is Nothing) AndAlso _ Not Me._mainForm.IsDisposed Then Me._mainForm.Activate() Return End If '検索ウィンドウを作成し、表示する Me._mainForm = New FindForm Me._mainForm.RichTextBox = Me._host.RichTextBox Me._mainForm.Owner = Me._host.MainForm Me._mainForm.Show() End Sub End Class End Namespace }} #code(csharp){{ using System; using System.Windows.Forms; namespace FindString { public class FindString : Plugin.IPlugin { private Plugin.IPluginHost _host; private FindForm _mainForm; //IPluginのメンバ public string Name { get { return "文字列の検索"; } } public string Description { get { return "編集中の文章から指定された文字列を検索します。"; } } public Plugin.IPluginHost Host { get { return _host; } set { _host = value; } } public string Version { get { //自分自身のAssemblyを取得し、バージョンを返す System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); System.Version ver = asm.GetName().Version; return ver.ToString(); } } public void Run() { //フォームが表示されていれば、アクティブにして終了 if (this._mainForm != null && !this._mainForm.IsDisposed) { this._mainForm.Activate(); return; } //検索ウィンドウを作成し、表示する this._mainForm = new FindForm(); this._mainForm.RichTextBox = this._host.RichTextBox; this._mainForm.Owner = this._host.MainForm; this._mainForm.Show(); } } } }} RunメソッドでFindFormを表示しているだけで、特に問題はないでしょう。(表示する前にRichTextBoxの設定と、オーナーウィンドウの設定を行っています。) ***メインアプリケーションの作成 [#v0d4f190] ようやくここまでたどり着きました。いよいよプラグインを使用するホストのアプリケーションを作成します。 ホストアプリケーションは、Windowsアプリケーションプロジェクトとして作成し(名前は、"MainApplication"とします)、"Plugin.dll"を参照に追加します。 フォームには、RichTextBoxと、メニュー、さらにメッセージを表示するためのStatusBarコントロールを配置します。変更するフォームのプロパティと、フォームに配置するコントロールとそのプロパティ(そしてイベント)の一覧を以下に示します。 #pre{{ コントロール: Form Name: Form1 Menu: mainMenu Loadイベント: Form1_Load コントロール: RichTextBox Name: mainRichTextBox Dock: Fill コントロール: StatusBar Name: mainStatusbar コントロール: MainMenu Name: mainMenu コントロール: MenuItem Name: menuPlugins Text: プラグイン(&P) コントロール: MenuItem Name: menuHelp Text: ヘルプ(&H) コントロール: MenuItem Name: menuAbout Text: バージョン情報(&A)... Clickイベント: menuAbout_Click }} 次に前号で作成したPluginInfoクラスをプロジェクトに追加します。ただし、CreateInstanceメソッドでIPluginHostオブジェクトをIPluginHost.Hostプロパティに設定するように変更しています。 PluginInfoクラスは次のようなコードです。 #code(vbnet){{ Imports System ''' <summary> ''' プラグインに関する情報 ''' </summary> Public Class PluginInfo Private _location As String Private _className As String ''' <summary> ''' PluginInfoクラスのコンストラクタ ''' </summary> ''' <param name="path">アセンブリファイルのパス</param> ''' <param name="cls">クラスの名前</param> Private Sub New(ByVal path As String, ByVal cls As String) Me._location = path Me._className = cls End Sub ''' <summary> ''' アセンブリファイルのパス ''' </summary> Public ReadOnly Property Location() As String Get Return _location End Get End Property ''' <summary> ''' クラスの名前 ''' </summary> Public ReadOnly Property ClassName() As String Get Return _className End Get End Property ''' <summary> ''' 有効なプラグインを探す ''' </summary> ''' <returns>有効なプラグインのPluginInfo配列</returns> Public Shared Function FindPlugins() As PluginInfo() Dim plugins As New System.Collections.ArrayList 'IPlugin型の名前 Dim ipluginName As String = _ GetType(Plugin.IPlugin).FullName 'プラグインフォルダ Dim folder As String = _ System.IO.Path.GetDirectoryName( _ System.Reflection.Assembly. _ GetExecutingAssembly().Location) folder += "\plugins" If Not System.IO.Directory.Exists(folder) Then Throw New ApplicationException( _ "プラグインフォルダ""" + folder + _ """が見つかりませんでした。") End If '.dllファイルを探す Dim dlls As String() = _ System.IO.Directory.GetFiles(folder, "*.dll") Dim dll As String For Each dll In dlls Try 'アセンブリとして読み込む Dim asm As System.Reflection.Assembly = _ System.Reflection.Assembly.LoadFrom(dll) Dim t As Type For Each t In asm.GetTypes() 'アセンブリ内のすべての型について、 'プラグインとして有効か調べる If t.IsClass And t.IsPublic And _ Not t.IsAbstract And _ Not (t.GetInterface(ipluginName) Is Nothing _ ) Then 'PluginInfoをコレクションに追加する plugins.Add(New PluginInfo(dll, t.FullName)) End If Next t Catch End Try Next dll 'コレクションを配列にして返す Return CType(plugins.ToArray( _ GetType(PluginInfo)), PluginInfo()) End Function 'FindPlugins ''' <summary> ''' プラグインクラスのインスタンスを作成する ''' </summary> ''' <returns>プラグインクラスのインスタンス</returns> Public Function CreateInstance( _ ByVal host As Plugin.IPluginHost) As Plugin.IPlugin Try 'アセンブリを読み込む Dim asm As System.Reflection.Assembly = _ System.Reflection.Assembly.LoadFrom(Me.Location) 'クラス名からインスタンスを作成する Dim plugin As Plugin.IPlugin = _ CType(asm.CreateInstance(Me.ClassName), _ Plugin.IPlugin) 'IPluginHostの設定 plugin.Host = host Return plugin Catch End Try End Function End Class }} #code(csharp){{ using System; namespace MainApplication { /// <summary> /// プラグインに関する情報 /// </summary> public class PluginInfo { private string _location; private string _className; /// <summary> /// PluginInfoクラスのコンストラクタ /// </summary> /// <param name="path">アセンブリファイルのパス</param> /// <param name="cls">クラスの名前</param> private PluginInfo(string path, string cls) { this._location = path; this._className = cls; } /// <summary> /// アセンブリファイルのパス /// </summary> public string Location { get {return _location;} } /// <summary> /// クラスの名前 /// </summary> public string ClassName { get {return _className;} } /// <summary> /// 有効なプラグインを探す /// </summary> /// <returns>有効なプラグインのPluginInfo配列</returns> public static PluginInfo[] FindPlugins() { System.Collections.ArrayList plugins = new System.Collections.ArrayList(); //IPlugin型の名前 string ipluginName = typeof(Plugin.IPlugin).FullName; //プラグインフォルダ string folder = System.IO.Path.GetDirectoryName( System.Reflection.Assembly .GetExecutingAssembly().Location); folder += "\\plugins"; if (!System.IO.Directory.Exists(folder)) throw new ApplicationException( "プラグインフォルダ\"" + folder + "\"が見つかりませんでした。"); //.dllファイルを探す string[] dlls = System.IO.Directory.GetFiles(folder, "*.dll"); foreach (string dll in dlls) { try { //アセンブリとして読み込む System.Reflection.Assembly asm = System.Reflection.Assembly.LoadFrom(dll); foreach (Type t in asm.GetTypes()) { //アセンブリ内のすべての型について、 //プラグインとして有効か調べる if (t.IsClass && t.IsPublic && !t.IsAbstract && t.GetInterface(ipluginName) != null) { //PluginInfoをコレクションに追加する plugins.Add( new PluginInfo(dll, t.FullName)); } } } catch { } } //コレクションを配列にして返す return (PluginInfo[]) plugins.ToArray(typeof(PluginInfo)); } /// <summary> /// プラグインクラスのインスタンスを作成する /// </summary> /// <returns>プラグインクラスのインスタンス</returns> public Plugin.IPlugin CreateInstance(Plugin.IPluginHost host) { try { //アセンブリを読み込む System.Reflection.Assembly asm = System.Reflection.Assembly.LoadFrom(this.Location); //クラス名からインスタンスを作成する Plugin.IPlugin plugin = (Plugin.IPlugin) asm.CreateInstance(this.ClassName); //IPluginHostの設定 plugin.Host = host; return plugin; } catch { return null; } } } } }} IPluginHostインターフェイスは、フォームクラスで実装します。また、プラグインの読み込みと、メニューへの表示はフォームのLoadイベントハンドラで行い、メニューを選択することにより、プラグインを実行できるようにします。 以下に変更を加えたフォームクラスの主要部分のコード(Windowsフォームデザイナが作成したコードを除く)を示します。 #code(vbnet){{ Public Class Form1 Inherits System.Windows.Forms.Form Implements Plugin.IPluginHost '(省略) 'IPluginの配列 Dim plugins() As Plugin.IPlugin 'IPluginHostの実装 Public ReadOnly Property MainForm() As Form _ Implements Plugin.IPluginHost.MainForm Get Return Me End Get End Property Public ReadOnly Property RichTextBox() As RichTextBox _ Implements Plugin.IPluginHost.RichTextBox Get Return mainRichTextBox End Get End Property Public Sub ShowMessage(ByVal plugin As Plugin.IPlugin, _ ByVal msg As String) _ Implements Plugin.IPluginHost.ShowMessage 'ステータスバーに表示する mainStatusbar.Text = msg End Sub 'メインフォームのLoadイベントハンドラ Private Sub Form1_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Load 'インストールされているプラグインを探す Dim pis As PluginInfo() = PluginInfo.FindPlugins() 'プラグインのインスタンスを取得する Me.plugins = New Plugin.IPlugin(pis.Length - 1) {} Dim i As Integer For i = 0 To (Me.plugins.Length) - 1 Me.plugins(i) = pis(i).CreateInstance(Me) Next i 'プラグインを実行するメニューを追加する Dim plugin As Plugin.IPlugin For Each plugin In Me.plugins Dim mi As New MenuItem(plugin.Name, _ AddressOf menuPlugin_Click) Me.menuPlugins.MenuItems.Add(mi) Next plugin End Sub 'プラグインのメニューがクリックされた時 Private Sub menuPlugin_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles menuPlugins.Click Dim mi As MenuItem = CType(sender, MenuItem) 'クリックされたプラグインを探す '(同じ名前のプラグインが複数あると困ったことに...) Dim plugin As Plugin.IPlugin For Each plugin In Me.plugins If mi.Text = plugin.Name Then 'クリックされたプラグインを実行する plugin.Run() Return End If Next plugin End Sub 'プラグインのバージョンを表示する Private Sub menuAbout_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles menuAbout.Click Dim msg As String = "インストールされているプラグイン" + vbLf _ + "(名前 : 説明 : バージョン)" + vbLf + vbLf Dim plugin As Plugin.IPlugin For Each plugin In Me.plugins msg += String.Format("{0} : {1} : {2}" + vbLf, _ plugin.Name, plugin.Description, plugin.Version) Next plugin MessageBox.Show(msg) End Sub End Class }} #code(csharp){{ namespace MainApplication { /// <summary> /// Form1 の概要の説明です。 /// </summary> public class Form1 : System.Windows.Forms.Form, Plugin.IPluginHost { //(省略) //IPluginの配列 Plugin.IPlugin[] plugins; //IPluginHostの実装 public Form MainForm { get { return (Form) this; } } public RichTextBox RichTextBox { get { return mainRichTextBox; } } public void ShowMessage(Plugin.IPlugin plugin, string msg) { //ステータスバーに表示する mainStatusbar.Text = msg; } //メインフォームのLoadイベントハンドラ private void Form1_Load(object sender, System.EventArgs e) { //インストールされているプラグインを探す PluginInfo[] pis = PluginInfo.FindPlugins(); //プラグインのインスタンスを取得する this.plugins = new Plugin.IPlugin[pis.Length]; for (int i = 0; i < this.plugins.Length; i++) this.plugins[i] = pis[i].CreateInstance(this); //プラグインを実行するメニューを追加する foreach (Plugin.IPlugin plugin in this.plugins) { MenuItem mi = new MenuItem(plugin.Name, new EventHandler(menuPlugin_Click)); this.menuPlugins.MenuItems.Add(mi); } } //プラグインのメニューがクリックされた時 private void menuPlugin_Click( object sender, System.EventArgs e) { MenuItem mi = (MenuItem) sender; //クリックされたプラグインを探す //(同じ名前のプラグインが複数あると困ったことに...) foreach (Plugin.IPlugin plugin in this.plugins) { if (mi.Text == plugin.Name) { //クリックされたプラグインを実行する plugin.Run(); return; } } } //プラグインのバージョンを表示する private void menuAbout_Click( object sender, System.EventArgs e) { string msg = "インストールされているプラグイン\n" + "(名前 : 説明 : バージョン)\n\n"; foreach (Plugin.IPlugin plugin in this.plugins) { msg += string.Format("{0} : {1} : {2}\n", plugin.Name, plugin.Description, plugin.Version); } MessageBox.Show(msg); } } } }} このメインアプリケーションを実行させるには、実行ファイルのあるフォルダに"plugins"というフォルダを作り、そこに前に作成したプラグイン"CountChars.dll"と"FindString.dll"をコピーしてください。うまくいくと、「プラグイン」メニューに「文字数取得」と「文字列の検索」が追加され、プラグインの機能を呼び出すことができるようになります。 以上でプラグイン機能を実現させる方法に関する解説はおしまいです。ここで紹介した知識を応用することにより、より複雑なプラグインも作成できるでしょう。この記事を読んで、プラグインを使ったアプリケーションを作ってみようと思われる方が一人でもいらっしゃるならばうれしいのですが。 **コメント [#x62023d5] #comment //これより下は編集しないでください #pageinfo([[:Category/.NET]],2004-08-31 (火) 06:00:00,DOBON!,2010-03-21 (日) 02:40:46,DOBON!) |