none
クラスのカスタム属性が取得できない。 RRS feed

  • 質問

  • さきほど、投稿して1日に2つも質問する形になってしまいましたが、原因は予想できるのですが、対処法がわかりません。

    まず、A.exeから、リフレクションを使用して、B.dllとC.dllのなかのPluginClassAttributeという独自定義のカスタム属性を付けたクラス(E.dllに存在し、exe、dll両方から参照を持っている)をインスタンス化したいと考えています。

    しかし、B.dllにはA.exeからの参照がない、D.dllの参照を持っており、この時、例外が発生しておりました。これについては、前の質問で解決しております。(http://social.msdn.microsoft.com/Forums/ja-JP/csharpgeneralja/thread/ee7807ba-e598-4dc2-abfc-50cb35297109

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
    Assembly asm = null;
          try
          {
            //GACに登録されているアセンブリ名の場合
            asm=Assembly.LoadFrom(args.Name);
            return asm;
          }
          catch (FileNotFoundException e)
          {//GACに登録されていないならば、プラグインフォルダから探す。
            foreach (string directry in Directries.Values)
            {
              try
              {
    
                if (!Path.IsPathRooted(directry))
                {
                  Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), directry);
                }
                string[] files = Directory.GetFiles(Path.GetFullPath(directry), "*.dll", SearchOption.AllDirectories);
                try
                {
                  foreach (var file in files)
                  {
                    asm = Assembly.LoadFrom(file);
                    //アセンブリ名が、読み込み対象と等しいかどうか
                    if (asm.GetName().FullName == args.Name)
                    {
                      return asm;
                    }
                  }
                }
                catch (Exception) {}//例外を出さずに続けさせる
              }
              catch (Exception) {}//例外を出さずに続けさせる
            }
            return null;
    }
    

    以上のように、書くことで、B.dllの参照は解決できました。この時にB.dll内のクラスに対して、GetCustomAttributesをすると、E.dllで定義された、属性を取得することができるのですが、

    C.dll内のクラスに対してGetCustomAttributesをすると、E.dllで定義された属性はおろか、GuidAttributesさえも取得できません。

    他のdllを読み込んでいるから、バージョンの誤差などできるのかと、思うのですが、その場合、.Net FrameWork内に存在する、GuidAttributesは取得できると思うのです。

    なぜなのでしょう?

    2011年4月12日 10:25

回答

  • 書いてるコードがむちゃくちゃです。エラーありきで例外を無視せず、内容を確かめましょう。

    GuidAttributeも得られないのは当たり前です。GuidAttributeはmscorlib.dllに定義されていると思いますがこのDLLの読み込みには成功していますか? Assembly.LoadFrom()の引数はファイルへのパスでありAssemblyNameではありません。AssemblyNameを引数にとるのはAssembly.Load()です。

    ドキュメントをちゃんと読むようにしましょう。

    それとやたらめったらDLLを読み込み過ぎです。DLLを読み込むとき、依存DLLが見つからないと再度AppDomain.AssemblyResolveイベントが発生します。ここでさらに無関係なDLLを読み込むと収拾がつかなくなります。

    • 回答としてマーク 山本春海 2011年4月28日 5:45
    2011年4月12日 12:15
  • AssemblyResolve イベントにこの処理を本当に書かないといけないのですか?
    佐祐理さんが指摘されているように、何度か呼ばれるリスクもありますし、本当にここに必要な処理なのか疑問を感じます。

    きちんとドキュメントを読み、意味を理解し、適切なコードを書くようにしてください。
    それができない内にプラグイン形式とか、リフレクションとか異様に凝ったコードを書くと、なかなか落ち着きませんよ。
    (基本がわかっていない状態でいろいろとやると、動かないし、不具合を埋め込むし、安定するまでに時間が多大にかかる)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2011年4月28日 5:45
    2011年4月12日 14:13
    モデレータ
  • そもそも、読み込みをカスタマイズする必要はあるのでしょうか?

    プラグインをプラグイン用のフォルダから読み込みたいのであれば、すべて .NET Framework に任せたほうが簡単にできますよ。プロジェクトに app.config を追加して、

    <configuration>
    
     <runtime>
    
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    
       <probing privatePath="MyPlugins"/>
    
      </assemblyBinding>
    
     </runtime>
    
    </configuration>
    
    
    
    

    と、設定します。後は、

    public IEnumerable<Type> GetMyPlugins(string filename)
    
    {
    
     return this.GetTypes(filename).Where(t => Attribute.IsDefined(t, typeof(MyPluginAttribute));
    
    }
    
    
    
    public Type[] GetAssemblies(string filename)
    
    {
    
     try
    
     {
    
      var asm = Assembly.Load(filename);
    
    
    
      return asm.GetTypes();
    
     }
    
     catch (xxxLoadException e)
    
     {
    
      return e.Types;
    
     }
    
    }
    
    
    
    

    と、読み込めばよいだけです。(携帯からそらで書いてるので、例外等は適当にかいてしまっています)

    このようなかんじで、MyPlugins フォルダにある MyPluginAttributes が設定された型だけを列挙することができると思います。

    • 回答としてマーク 山本春海 2011年4月28日 5:45
    2011年4月12日 23:43

すべての返信

  • 書いてるコードがむちゃくちゃです。エラーありきで例外を無視せず、内容を確かめましょう。

    GuidAttributeも得られないのは当たり前です。GuidAttributeはmscorlib.dllに定義されていると思いますがこのDLLの読み込みには成功していますか? Assembly.LoadFrom()の引数はファイルへのパスでありAssemblyNameではありません。AssemblyNameを引数にとるのはAssembly.Load()です。

    ドキュメントをちゃんと読むようにしましょう。

    それとやたらめったらDLLを読み込み過ぎです。DLLを読み込むとき、依存DLLが見つからないと再度AppDomain.AssemblyResolveイベントが発生します。ここでさらに無関係なDLLを読み込むと収拾がつかなくなります。

    • 回答としてマーク 山本春海 2011年4月28日 5:45
    2011年4月12日 12:15
  • AssemblyResolve イベントにこの処理を本当に書かないといけないのですか?
    佐祐理さんが指摘されているように、何度か呼ばれるリスクもありますし、本当にここに必要な処理なのか疑問を感じます。

    きちんとドキュメントを読み、意味を理解し、適切なコードを書くようにしてください。
    それができない内にプラグイン形式とか、リフレクションとか異様に凝ったコードを書くと、なかなか落ち着きませんよ。
    (基本がわかっていない状態でいろいろとやると、動かないし、不具合を埋め込むし、安定するまでに時間が多大にかかる)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2011年4月28日 5:45
    2011年4月12日 14:13
    モデレータ
  • そもそも、読み込みをカスタマイズする必要はあるのでしょうか?

    プラグインをプラグイン用のフォルダから読み込みたいのであれば、すべて .NET Framework に任せたほうが簡単にできますよ。プロジェクトに app.config を追加して、

    <configuration>
    
     <runtime>
    
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    
       <probing privatePath="MyPlugins"/>
    
      </assemblyBinding>
    
     </runtime>
    
    </configuration>
    
    
    
    

    と、設定します。後は、

    public IEnumerable<Type> GetMyPlugins(string filename)
    
    {
    
     return this.GetTypes(filename).Where(t => Attribute.IsDefined(t, typeof(MyPluginAttribute));
    
    }
    
    
    
    public Type[] GetAssemblies(string filename)
    
    {
    
     try
    
     {
    
      var asm = Assembly.Load(filename);
    
    
    
      return asm.GetTypes();
    
     }
    
     catch (xxxLoadException e)
    
     {
    
      return e.Types;
    
     }
    
    }
    
    
    
    

    と、読み込めばよいだけです。(携帯からそらで書いてるので、例外等は適当にかいてしまっています)

    このようなかんじで、MyPlugins フォルダにある MyPluginAttributes が設定された型だけを列挙することができると思います。

    • 回答としてマーク 山本春海 2011年4月28日 5:45
    2011年4月12日 23:43