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

アセンブリのタイプ取得について

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

以前、どぼん様が書かれた記事で、
プラグインテキストエディタというものがあったと思います。
それを参考にしながら勉強しているのですが、
有効なプラグインを見つけるところで悩んでいます。
Framework2.0ではReflectionOnlyLoadFromが使えるとのことでしたので、

System.Reflection.Assembly asm = 
System.Reflection.Assembly.LoadFrom(dll);
foreach (Type t in asm.GetTypes())
{
  有効なプラグインかどうか判断する
}

というところを、
System.Reflection.Assembly asm = 
System.Reflection.Assembly.ReflectionOnlyLoadFrom(dll);
foreach (Type t in asm.GetTypes())
{
  有効なプラグインかどうか判断する
}
としてみたところ、asm.GetTypes()で
例外System.Reflection.ReflectionTypeLoadExceptionが発生してしまいました。
要は何も読み込めなかったということらしいのですが、
ReflectionOnlyLoadFromを利用した場合はアセンブリを実行できず、
asm.GetTypesを実行すると例外が発生するということなのでしょうか?
ここはどうすべきなのか、よくわからないでいます。
ご指導のほど、宜しくお願いします。
# 私にはご指導などとてもできませんが、私の環境で試してみたところ、以下の方法で上手く行きましたので、ご報告します。

Assembly.ReflectionOnlyLoadFrom()メソッドを使用して検査コンテキストにアセンブリ(以下、アセンブリAと表記)をロードした場合、当該検査コンテキスト内にアセンブリAが依存しているアセンブリがロードされていない場合は、Assembly.GetTypes()メソッドを使用しても型が読み込めずにReflectionTypeLoadExceptionがスローされるようです。

私の環境で試してみたところ、当該ReflectionTypeLoadExceptionオブジェクトのLoaderExceptionsプロパティの値はFileLoadExceptionオブジェクトで、依存関係にあるアセンブリが読み込めなかった、という内容でした。

対策としては、AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolveイベントハンドラ内で、当該依存関係にあるアセンブリをロードすると、上手く行きました。

参考文献
 http://msdn.microsoft.com/ja-jp/library/system.reflection.assembly.gettypes.aspx
 日経BPソフトプレス刊「プログラミングVisualBasic2005言語編(下)」、pp.360 - 362. 
 
Private Sub testG()
  Dim ss As String = "あああ.dll"
  Dim asm2 As Assembly = Assembly.ReflectionOnlyLoadFrom(ss)
 
  AddHandler AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve, AddressOf Me.AppDomain_ReflectionOnlyAssemblyResolve
  Try
   Dim tys() As Type = asm2.GetTypes()
   Console.WriteLine("うまくいった")
   For Each ty As Type In tys
    If ty IsNot Nothing Then
     Console.WriteLine(ty.FullName)
    Else
     Console.WriteLine("Nothing")
    End If
   Next
  Catch ex As ReflectionTypeLoadException
   For Each ty As Type In ex.Types
    If ty IsNot Nothing Then
     Console.WriteLine(ty.FullName)
    Else
     Console.WriteLine("Nothing")
    End If
   Next
   For Each e As Exception In ex.LoaderExceptions
    Console.WriteLine(e.GetType.Name + " - " + e.Message)
   Next
  End Try
 
End Sub
 
Private Function AppDomain_ReflectionOnlyAssemblyResolve(ByVal sender As Object, ByVal e As ResolveEventArgs) As Assembly
  Console.WriteLine("ReflectionOnlyAssemblyResolve - {0}", e.Name)
  Return Assembly.ReflectionOnlyLoad(e.Name)
End Function
追記です。
私のコードに不十分な箇所がありました。
 
GACに存在しないアセンブリで、なおかつアプリケーション実行ディレクトリにも存在しないアセンブリ(以下、アセンブリCと表記)が存在したとします。
 
このとき、アセンブリA(アセンブリAの定義は前回の私の投稿を参照)がアセンブリCに依存していた場合、私の前回のコードはそのままでは上手くいきません。m(_ _)m
 
対策としては、Assembly.ReflectionOnlyLoadFromメソッドを使用してアセンブリCを予め検査コンテキストにロードしておくか、あるいはAppDomain.CurrentDomain.ReflectionOnlyAssemblyResolveイベントハンドラ内で、以下のような処理を記述する方法が考えられます。
 
# 但し、いずれにせよAssembly.CreateInstanceメソッドやActivator.CreateInstanceメソッドを使用してインスタンスを生成することができないため、プラグイン用途での使用は制限されます。
 
Private Function AppDomain_ReflectionOnlyAssemblyResolve(ByVal sender As Object, ByVal e As ResolveEventArgs) As Assembly
  Console.WriteLine("ReflectionOnlyAssemblyResolve - {0}", e.Name)
 
  Dim asm As Assembly = Nothing
  Try
   asm = Assembly.ReflectionOnlyLoad(e.Name)
   Return asm
  Catch ex As FileNotFoundException
   ' 読み込もうとするアセンブリがアプリケーション実行ディレクトリやGACに存在しない場合はここに来る。
   Console.WriteLine("  読み直し {0}", e.Name)
   ' アプリケーション実行ディレクトリ配下のPluginディレクトリにアセンブリCが存在することを前提とする。
   Dim ss As String = Application.StartupPath + "\Plugin"
  
   For Each fname As String In Directory.GetFiles(ss, "*.dll")
    asm = Assembly.ReflectionOnlyLoadFrom(fname)
    If asm.GetName.FullName = e.Name Then
     Return asm
    End If
   Next
 
  Finally
   If asm IsNot Nothing Then
    Console.WriteLine("  ReflectionOnly = {0}、GAC = {1}", asm.ReflectionOnly, asm.GlobalAssemblyCache)
   Else
    Console.WriteLine("  失敗")
   End If
  End Try
 
  Return Nothing
 
End Function
 
P.S. ご存知かもしれませんが、VB ⇔ C# 言語変換には例えば
 http://labs.developerfusion.co.uk/convert/vb-to-csharp.aspx
が利用可能です。コードがサーバーにPostされてしまうという問題はありますが。

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