• 検索結果がありません。

プラグイン

N/A
N/A
Protected

Academic year: 2021

シェア "プラグイン"

Copied!
13
0
0

読み込み中.... (全文を見る)

全文

(1)

■ プラグイン詳細・2 ■

~ プラグイン機能を持つテキストエディタの作成 ~ ■ はじめに

Adobe Photoshop や Becky! Internet Mail 等のアプリケーションでは「プラグイン」(又は、「アドイン」、 「エクステンション」等)と呼ばれるプログラムをインストールする事に依り、機能を拡張する事が出 来る。此の記事では此の様なプラグイン機能を持ったアプリケーションの作り方を、プラグイン対応の テキストエディタを作成する事に依り、説明する。 此処で紹介するプラグイン機能は、Becky!の様に、プラグイン本体で有る DLL ファイルを指定された フォルダにコピーする事に依り、プラグインを使用するアプリケーション(ホスト)が自動的にプラグ インを認識すると謂う物で有る。 プラグイン機能の有るテキストエディタ 猶、プラグイン機能の解説が目的の為、テキストエディタはフォームにRichTextBox を貼り付けた丈の 貧弱な物ですので、テキストエディタ作成の参考にはなりません。 ■ 必要な環境

サンプルは Visual Studio .NET 2003 で作成され、.NET Framework 1.1 で動作確認をして居る が、.NET Framework 1.0 でも問題ないはずで有る。

■ プラグイン機能を実現する為の基本的な考え方

通常 DLL アセンブリファイルを別のアセンブリから使用するには、予め其のアセンブリを参照に追加

しておく必要が有る(Visual Studio .NET の場合は[プロジェクト]→[参照の追加...]を実行し、ダ

(2)

イアログから追加出来る。.NET SDK の場合はコンパイルオプション「/reference」を使います)。併し プラグインの場合はコンパイル時に参照する事ができない為、実行時に読み込む必要が有る。此れを可 能にするのが、「リフレクション」で有る。 リフレクションを利用すれば、実行時に DLL 内の型のメンバにアクセスする事が出来る。よって、プ ラグインの機能をホストから呼び出す時のメソッド名、パラメータ、戻り値等を「約束事」と仕て決め ておけば、ホストからプラグインの機能を呼び出す事が出来る様に成る。 以上の様な方法でプラグイン機能の実現は十分可能で有る。併し、ホストとプラグインの間で決められ た「約束事」があいまいでは、分かりづらく、危険で有る。此の「約束」を確実にする為には、インタ ーフェイスを用いるのが良いでしょう。詰り、プラグインが必ず持つべきメソッドやプロパティをイン ターフェイスで定義して置き、プラグインは此のインターフェイスを実装した物でなければ成らないと するので有る。此の様にしておけば、プラグインを作成する側では何をしなければ成らないのか明確に なり、ホストの側ではプラグインを指定したインターフェイスの型と仕て扱う事が出来る。 同様にホストで実装すべきインターフェイスを定義しておく事に依り、プラグインからホストの機能を 呼び出したり、結果をコールバックする事が容易に成る。 ■ インターフェイスの作成

先ずは、インターフェイスを作成する。Visual Studio .NET では、クラスライブラリのプロジェクトを

作成し、プラグインで実装すべきインターフェイス(IPlugin)と、ホストで実装すべきインターフェ イス(IPluginHost)を定義する。.NET SDK の場合は、「/target:library」オプションを使用してコン パイルする。サンプルでは、プロジェクト「Plugin」でプラグインとホストのインターフェイスを定義 し、「Plugin.dll」と謂うファイル名で出力して居る。以下に其のコードの一部を抜粋する。 「IPlugin.vb」 (VB.NET)、「IPlugin.cs」 (C#) Visual Basic Namespace Plugin ' <summary> ' プラグインで実装するインターフェイス ' </summary>

Public Interface IPlugin ' <summary> ' プラグインの名前 ' </summary>

ReadOnly Property Name() As String ' <summary> ' プラグインを実行する ' </summary> Sub Run() ' <summary> ' プラグインのインスタンス作成直後に呼び出されるメソッド ' </summary> ' <param name="host">プラグインのホスト</param> Sub Initialize(ByVal host As IPluginHost)

'(以下省略) End Interface

(3)

' <summary>

' プラグインのホストで実装するインターフェイス ' </summary>

Public Interface IPluginHost ' <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 C# namespace Plugin { // <summary> // プラグインで実装するインターフェイス // </summary>

public interface IPlugin {

// <summary> // プラグインの名前 // </summary> string Name {get;} // <summary> // プラグインを実行する // </summary> void Run(); // <summary> // プラグインのインスタンス作成直後に呼び出されるメソッド // </summary> // <param name="host">プラグインのホスト</param> void Initialize(IPluginHost host);

//(以下省略) }

// <summary>

// プラグインのホストで実装するインターフェイス // </summary>

public interface IPluginHost {

// <summary>

// ホストの RichTextBox コントロール // </summary>

(4)

RichTextBox RichTextBox {get;} // <summary> // ホストでメッセージを表示する // </summary> // <param name="plugin">メソッドを呼び出すプラグイン</param> // <param name="msg">表示するメッセージ</param>

void ShowMessage(IPlugin plugin, string msg); //(以下省略) } } ■ プラグインの作成 次に、IPlugin インターフェイスを実装する事に依り、プラグインを作成する。先程と同様にクラスラ イブラリのプロジェクトを新規作成し、「Plugin.dll」を参照に加え、IPlugin を実装したクラスを作成 する。サンプルには3 つのプラグインのプロジェクト(「CountChar」、「FindString」、「FileHistory」) が含まれて居る。 此のうち最も単純な、文章の文字数を数える丈のプラグインで有る CountChar プラグインのコードの 一部を以下に示す。 「CountChars.vb」 (VB.NET)、「CountChars.cs」 (C#) Visual Basic Namespace CountChars ' <summary> ' 文字数を表示する為のプラグイン ' </summary>

Public Class CountChars Implements Plugin.IPlugin

Private _host As Plugin.IPluginHost

Public ReadOnly Property Name() As String _ Implements Plugin.IPlugin.Name

Get

Return "文字数取得" End Get

End Property

Public Sub Initialize(ByVal host As Plugin.IPluginHost) _ Implements Plugin.IPlugin.Initialize Me._host = host End Sub '(以下省略) End Class End Namespace C#

(5)

namespace CountChars {

// <summary>

// 文字数を表示する為のプラグイン // </summary>

public class CountChars : Plugin.IPlugin {

private Plugin.IPluginHost _host; public string Name

{ get { return "文字数取得"; } }

public void Initialize(Plugin.IPluginHost host) { this._host = host; } //(以下省略) } } FindString プラグインは、フォームを表示するプラグインのサンプルで有る。検索ダイアログを表示し て、指定された文字列を検索する。FileHistory プラグインに関しては、後ほど説明する。 ■ ホストの作成 ホストアプリケーションは、Windows アプリケーションと仕て作成し、フォームに RichTextBox と、 MainMenu、StatusBar コントロールを配置する。更に、IPluginHost インターフェイスを実装する。 サンプルでは、プロジェクト「TextEditorForm」がホストで有る。 Visual Basic Namespace MainApplication Public Class TextEditorForm

Inherits System.Windows.Forms.Form Implements Plugin.IPluginHost

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 'ステータスバーに表示する

(6)

mainStatusbar.Text = msg End Sub '(以下省略) End Class End Namespace C# namespace PluginTextEditor {

public class TextEditorForm :

System.Windows.Forms.Form, Plugin.IPluginHost {

public RichTextBox RichTextBox { get { return mainRichTextBox; } }

public void ShowMessage(Plugin.IPlugin plugin, string msg) { //ステータスバーに表示する mainStatusbar.Text = msg; } //(以下省略) } } ホストの作成で問題と成るのは、有効なプラグインを何の様に探すか、然して、プラグインのインスタ ンスを何の様に作成するかの2 点でしょう。 有 効 な プ ラ グ イ ン を 探 す に は 、 指 定 さ れ た プ ラ グ イ ン フ ォ ル ダ に 有 る DLL フ ァ イ ル を Assembly.LoadFrom メソッドで読み込み、其の中に含まれて居る型を列挙し、Type.GetInterface メソ ッドに依りIPlugin インターフェイスを実装したクラスで有るか調べる事にする。 亦 プ ラ グ イ ン の イ ン ス タ ン ス を 作 成 す る に は 、Activator.CreateInstance メ ソ ッ ド や 、 Assembly.CreateInstance メソッド等を使用すれば良いでしょう。 此等の処理は、PluginInfo クラスで行って居る。 Visual Basic Imports System Namespace MainApplication ' <summary> ' プラグインに関する情報 ' </summary>

Public Class PluginInfo

Private _location As String Private _className As String ' <summary>

(7)

' 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( _

ByVal pluginDir As String) As PluginInfo()

Dim plugins As New System.Collections.ArrayList 'IPlugin 型の名前

Dim ipluginName As String = _

GetType(Plugin.IPlugin).FullName

If Not System.IO.Directory.Exists(pluginDir) Then Throw New ApplicationException( _

"プラグインフォルダ""" + pluginDir + _ """が見付かりませんでした。") End If '.dll ファイルを探す Dim dlls As String() = _ System.IO.Directory.GetFiles(pluginDir, "*.dll") Dim dll As String For Each dll In dlls Try 'アセンブリと仕て読み込む

Dim asm As System.Reflection.Assembly = _ System.Reflection.Assembly.LoadFrom(dll)

(8)

Dim t As Type

For Each t In asm.GetTypes()

'アセンブリ内の総ての型に付いて、 'プラグインと仕て有効か調べる

If t.IsClass AndAlso t.IsPublic AndAlso _ Not t.IsAbstract AndAlso _

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 ' <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) '初期化 plugin.Initialize(host) Return plugin Catch Return Nothing End Try End Function End Class End Namespace C# using System; namespace PluginTextEditor { // <summary> // プラグインに関する情報 // </summary>

(9)

{

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(string pluginDir) {

System.Collections.ArrayList plugins = new System.Collections.ArrayList(); //IPlugin 型の名前

string ipluginName = typeof(Plugin.IPlugin).FullName; if (!System.IO.Directory.Exists(pluginDir))

throw new ApplicationException(

"プラグインフォルダ¥"" + pluginDir + "¥"が見付かりませんでした。"); //.dll ファイルを探す string[] dlls = System.IO.Directory.GetFiles(pluginDir, "*.dll"); foreach (string dll in dlls) { try

(10)

{

//アセンブリと仕て読み込む

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); //初期化 plugin.Initialize(host); return plugin; } catch { return null; } } } } ■ 応用

(11)

以上がプラグイン機能を実現する基本的な方法で、同様の事が下に示した参考資料の 1~6 のリンクで も紹介されて居る。此処からは其の応用と仕て、使えそうなアイデアを幾つか紹介する。 IPluginHost にイベントを追加する 例えばエディタでファイルを開いた時や保存した時に、其の事をプラグイン側で知る事ができれば、プ ラグインで出来る事が広がります。此処では其の例と仕て、IPluginHost インターフェイスにファイル を開いた直後に発生するOpenedFile イベントを追加する。 Visual Basic Namespace Plugin ' <summary> ' OpenedFile イベントのデリゲート ' </summary>

Public Delegate Sub OpenedFileEventHandler( _

ByVal sender As Object, ByVal e As OpenedFileEventArgs) Public Interface IPluginHost

' <summary>

' ファイルを開いた直後に発生するイベント ' </summary>

Event OpenedFile As OpenedFileEventHandler End Interface '(以下省略) End Namespace C# namespace Plugin { // <summary> // OpenedFile イベントのデリゲート // </summary>

public delegate void OpenedFileEventHandler( object sender, OpenedFileEventArgs e); public interface IPluginHost

{

// <summary>

// ファイルを開いた直後に発生するイベント // </summary>

event OpenedFileEventHandler OpenedFile; } //(以下省略) } 此のイベントを使ったプラグインの例が、サンプルのプロジェクト「FileHistory」で有る。此のプラグ インでは、エディタで開かれたファイルのパスを保存して居る。 プラグインでMenuItem オブジェクトを作成する プラグインをメニューに表示する場合、其のMenuItem はホストで作成するのが普通でしょう(少なく

(12)

とも下に挙げた参考資料のサンプルではそう成って居る)。併し、IPlugin インターフェイスに MenuItem 型のプロパティを追加し、プラグイン側で MenuItem オブジェクトを作成すると謂う方法 も有る。プラグインが自分のMenuItem を管理すべきと考えれば、むしろ此の方が良いでしょう。 サンプルのFileHistory プラグインではサブメニューを持つ MenuItem を作成し、下図の様にサブメニ ューで選択したファイルを開ける様にして居る。 サブメニューを持つプラグイン プラグインの設定を行う プラグインによっては、独自の設定が必要な物も有るでしょう。其処で、IPlugin インターフェイスに ShowSetupDialog メソッドを追加し、プラグインの設定ダイアログを表示出来る様にする。更に HasSetupDialog プロパティを追加し、設定ダイアログが有るかないか取得出来る様にすれば、事前に 設定の有無を知る事ができ、下図の様に、ユーザーがプラグインを選択した時に設定があれば[設定] ボタンを有効に、なければ無効にするといった事が出来る様に成る。 プラグインの設定ダイアログ サンプル内で設定の有るプラグインの例は、FileHistory プラグインで有る。

(13)

補足:アプリケーションの設定がTabControl で行われる時は、インターフェイスが自分の設定の為の TabPage オブジェクトを作成し、アプリケーションの設定に追加出来る様すると謂う方法も有るでしょ う。 ■ まとめ 此の記事では、.NET Framework に依りプラグイン機能を実現させる方法を、同機能を持ったテキスト エディタの作成を例に解説しました。プラグイン機能実現のポイントを簡単にまとめると下記の様に成 る。 ・プラグインは DLL と仕て作成し、ホストからはリフレクションを使ってプラグインの機能を呼び出 す。 ・プラグイン及びホストで必ず実装すべきインターフェイスを予め作成しておけば、あいまいさを排除 する事ができ、有益で有る。

参照

関連したドキュメント

うのも、それは現物を直接に示すことによってしか説明できないタイプの概念である上に、その現物というのが、

 米国では、審査経過が内在的証拠としてクレーム解釈の原則的参酌資料と される。このようにして利用される資料がその後均等論の検討段階で再度利 5  Festo Corp v.

■使い方 以下の5つのパターンから、自施設で届け出る症例に適したものについて、電子届 出票作成の参考にしてください。

に文化庁が策定した「文化財活用・理解促進戦略プログラム 2020 」では、文化財を貴重 な地域・観光資源として活用するための取組みとして、平成 32

適合 ・ 不適合 適 合:設置する 不適合:設置しない. 措置の方法:接続箱

 事業アプローチは,貸借対照表の借方に着目し,投下資本とは総資産額

表 2.1-1 に米国の NRC に承認された AOO,ATWS,安定性,LOCA に関する主な LTR を示す。No.1 から No.5 は AOO または ATWS に関する LTR を,No.6 から No.9 は安定性に 関する

従って、こ こでは「嬉 しい」と「 楽しい」の 間にも差が あると考え られる。こ のような差 は語を区別 するために 決しておざ