DOBON.NET DOBON.NETプログラミング掲示板過去ログ

plugin機能でのMissingMethodException

環境/言語:[WindowsXP SP3 C# .NET Framework2.0]
分類:[.NET]

お世話になります。

プラグイン機能をもつアプリを作成しているのですが
Assembly.CreateInstance()でインスタンス化したプラグインクラスに
パラメータオブジェクトを渡すとMissingMethodExceptionが発生します。

Assembly.CreateInstance()ではなく、アプリ自体の参照設定に
プラグインのdllを追加し、newでインスタンス化した場合は
正常に処理が実行されます。

その部分の抜粋は以下です。
(1)の場合は、(3)の部分でMissingMethodExceptionが発生
(2)にした場合は、正常にプラグインが実行されます。

実際にMissingMethodExceptionが発生しているのは
プラグインクラスの中でPluginParameter.Connectionにアクセスした際です。

Assembly.CreateInstance()をActivator.CreateInstance()に変えても同様です。
new演算子とAssembly.CreateInstance()には、処理に差があるのでしょうか?



-------------------

//アセンブリを読み込む
Assembly asm = Assembly.LoadFrom(pi.Location);

/*** (1) ***/
//クラス名からインスタンスを作成する
IPluginHost plugin = (IPluginHost)asm.CreateInstance(pi.ClassName);
/*** (2) ***/
//IPluginHost plugin = new Exporter();


PluginParameter param = new PluginParameter(qc.Connection, SqlText, dt);

/*** (3) ***/
/*** ここでMissingMethodException ***/
plugin.Run(param);

-------------------


PluginParameterクラスの定義には
 public DbConnection Connection{ get; }
のようにpublicプロパティgetで定義してあります。


MissingMethodException発生時のスタックとレースは以下です。

場所 MyPackage.Refrection.Exporter.Run(PluginParameter param)
場所 MyPackage.frmMDIMain.tsmi_Click(Object sender, EventArgs e) 場所 C:\Workspace\VisualStudio2008\Projects\App\frmMDIMain_PrivateMethod.cs:行 215
場所 System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
場所 System.Windows.Forms.ToolStripMenuItem.OnClick(EventArgs e)
場所 System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
場所 System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
場所 System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
場所 System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
場所 System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
場所 System.Windows.Forms.ToolStripDropDown.OnMouseUp(MouseEventArgs mea)
場所 System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
場所 System.Windows.Forms.Control.WndProc(Message& m)
場所 System.Windows.Forms.ScrollableControl.WndProc(Message& m)
場所 System.Windows.Forms.ToolStrip.WndProc(Message& m)
場所 System.Windows.Forms.ToolStripDropDown.WndProc(Message& m)
場所 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
場所 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
場所 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
場所 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
場所 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
場所 System.Windows.Forms.Application.Run(Form mainForm)
場所 MyPackage.Program.Main() 場所 C:\Workspace\VisualStudio2008\Projects\App\Program.cs:行 17
場所 System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
場所 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
場所 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
場所 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
場所 System.Threading.ThreadHelper.ThreadStart()


使用するDLL等をリビルド、参照設定の確認は行いましたが
解決に至りませんでした。
何か方法があれば助言ください。

よろしくお願いいたします。
■No27246に返信(pi-ta-さんの記事)

予想ですがIPluginHostにはRun(PluginParameter)がない為発生していると思われます。参照しnewするとクラス定義を参照することが出来るのでエラーにならないのではないでしょうか?参照しておいてもLoadされたクラスが参照先のクラスと同じと判断することが出来ないのかもしれません。
shuさま、返信ありがとうございます。
IPluginの定義を書いていなかったので、申し訳ありません。

以下のような内容になります。

<IPlugin>
public interface IPluginHost
{
double Version { get; }
string Name { get; }
Image MenuIcon { get; }
Int32 Run(PluginParameter param);
}

<PluginHostImpl>
public abstract class PluginHostImpl : IPluginHost
{
public abstract double Version { get; }
public abstract string Name { get; }
public abstract Image MenuIcon { get; }
public abstract Int32 Run(PluginParameter param);
}

上記抽象クラスを継承しているのがExporterクラスとなります。
CreateInstanceおよび、newを受ける変数としてIPluginで宣言しています。

IPlugin.Run(PluginParameter)の定義がない場合はExporterクラスの
コンパイル時にエラーになるかなと思います。

後だし情報ですが、さらに
プラグインクラスのRun(PluginParameter)内部で、PluginParameter.Connectionにアクセスしなければ
MissingMethodExceptionは発生いたしません。(Connectionアクセス箇所をコメントするとか)


感覚だと、
「new」の場合はExporterクラスが参照しているDbConnectionもロードされている。
「Assembly.CreateInstance」だとExporterはロードされているが、DbConnectionがロードされていない。

ような感じです。

なので、newとAssembly.CreateInstance()との内部的な差異があるのかなぁと考えているところです。
少ないですが、確認観点を提示します。

・最後に IPlugin などを含むアセンブリをビルドした時点以降に、Exporter を含むアセンブリと利用するアセンブリはビルドされていますか?
 (両方が同一バージョンを利用しているか)
・Exporter.Run の中を空にしたときは例外が発生しないか。
 (Run の中で問題なのか、Run 自体を呼べていないのか)
2010/09/02(Thu) 14:11:13 編集(投稿者)

Azulean様、返信ありがとうございます。

それぞれの確認事項について

<本体のアセンブリ配置>
本体アプリ(exe)\pluginsフォルダに
 Exporter.dll
 IPluginHost.dll
を格納しています。
これらをAssemblyクラスでロード、インスタンスしています。


<本体の参照設定>
newを行う際に参照しているdllは、
 ExporterプロジェクトフォルダのRelease\Exporter.dll
 IPluginHostプロジェクトフォルダのRelease\IPluginHost.dll
 
 
<Exporter.Run(PluginParameter)について>
 Exporter.Run()メソッド内のコードをすべてコメントアウトする
 または、Connectionプロパティにアクセスする部分をコメントアウトすると
 Exporter.Run()の実行は正常に処理されます。

 Exporter.dllはIPluginHostプロジェクトのRelease\IPluginHost.dllを参照しています。



Exporterプロジェクトをリビルドし、ExporterプロジェクトのReleaseの
Exporter.dll、IPluginHost.dllをpluginsフォルダに格納しています。

参照先をそれぞれのプロジェクトのReleaseを参照していますが
pluginsフォルダのdllを参照するように変更しても同様の現象が発生します。

ファイル更新日付を見る限りでは同じdllを参照できているかと思います。
自己、解決しました。

方法ですが、pluginsフォルダにIPluginHost.dllを格納していたのが問題だったようです。

本体exeがIPluginHostインターフェースを型として宣言するため
exeと同じフォルダにIPluginHost.dllが存在し(VisualStudioがコピー)

Exporter.dllがIPluginHostを参照するため必要だと思い
pluginsフォルダにもIPluginHost.dllを配置(手動)していました。

exe
 |
plugins ---------+-- Exporter.dll
 |       |
IPluginHost.dll +-- IPluginHost.dll(これを削除)


plugins\IPluginHost.dllを削除したところ正常に処理されました。
プラグインdll(Exporter.dll)の配置には、プラグインをコールする本体フォルダに
プラグインが参照するdll(IPluginHost)を格納されていればいいんですね。

本体exeがdll(IPluginHost)を既にメモリにロード済みなので
プラグインdllがわざわざ自分自身でロードすることはないと
思っていればいいんでしょうか。

それにしてもこれでMissingMethodExceptionというのが腑に落ちませんが・・・

ちなみにexeと同階層にあるIPluginHost.dllをpluginsフォルダにコピーしてみると
MissingMethodExceptionが発生しました。(削除すると正常動作する)

ご返答頂きました方々、ありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板