14

提供されたディレクトリにアセンブリを再帰的にロードし、それらのカスタム属性コレクションを読み取るための小さなアプリケーションを作成しました。主に、DebuggableAttributeを読み取って、IsJITTrackingEnabledおよびIsJITOptimizerDisabledの設定を判別し、アセンブリがリリース用に最適化されているかどうかを判別します。

私の現在のコードはAssembly.LoadFromを実行して、アセンブリへのパス全体を渡し、それをロードします。次に、アセンブリに対してGetCustomAttributesを実行して、デバッグ可能な属性を取得します。問題は、各アセンブリが現在のappdomainにロードされることです。したがって、別のフォルダが同じアセンブリを使用する場合、最初にロードされた参照を使用するだけです。アセンブリをロードし、必要なプロパティを読み取ってから、アンロードできるようにしたいと思います。新しいappdomainを作成し、そこにアセンブリをロードしてから、アセンブリのあとでアンロードしてみましたが、役に立ちませんでした。

私はこれが可能でなければならないことを知っていますが、私は途方に暮れています。どんな助けでも大歓迎です。他にも必要な情報を提供させていただきます。

4

4 に答える 4

20

簡単な答えは、いいえ、あなたが求めていることをする方法はありません。

より長い答えはこれです:Assembly.ReflectionOnlyLoad()「反射のみ」のロードコンテキストを使用する特別なアセンブリロードメソッドがあります。これにより、実行できないアセンブリをロードできますが、メタデータを読み取ることはできます。

あなたの場合(そして、明らかに、私が自分で思いつく可能性のあるすべてのユースケースで)、それは実際にはそれほど役に立ちません。この種のアセンブリから型付き属性を取得することはできません。。のみCustomAttributeDataです。そのクラスは、特定の属性をフィルタリングするための良い方法を提供していません(私が思いついた最善の方法は、それを文字列にキャストして使用することでしたStartsWith("[System.Diagnostics.Debuggable");

さらに悪いことに、リフレクションのみのロードでは依存関係アセンブリはロードされませんが、手動でロードする必要があります。それはあなたが今していることよりも客観的にそれを悪化させます。少なくとも今は、依存関係が自動的に読み込まれます。

(また、私の以前の回答はMEFに言及していました。私は間違っていました。これを機能させるために、MEFには大量のカスタムリフレクションコードが含まれているようです。)

最終的に、一度ロードされたアセンブリをアンロードすることはできません。このMSDNの記事で説明されているように、アプリドメイン全体をアンロードする必要があります。

アップデート:

コメントに記載されているように、リフレクションのみのロード(および通常のロード)を介して必要な属性情報を取得できましたが、型指定された属性メタデータがないため、深刻な問題が発生します。

通常のアセンブリコンテキストにロードすると、必要な情報を簡単に取得できます。

var d = a.GetCustomAttributes(typeof(DebuggableAttribute), false) as DebuggableAttribute;
var tracking = d.IsJITTrackingEnabled;
var optimized = !d.IsJITOptimizerDisabled;

リフレクションのみのコンテキストにロードすると、いくつかの作業を行うことができます。属性コンストラクターがとった形式を理解し、デフォルト値が何であるかを理解し、その情報を組み合わせて各プロパティの最終値を作成する必要があります。次のように必要な情報を取得します。

var d2 = a.GetCustomAttributesData()
         .SingleOrDefault(x => x.ToString()
                                .StartsWith("[System.Diagnostics.DebuggableAttribute"));

そこから、ConstructorArgumentsどのコンストラクターが呼び出されたかを確認する必要があります。これは1つの引数を持つか、これは2つの引数を持つものです。次に、適切なパラメータの値を使用して、関心のある2つのプロパティが取得した値を把握できます。

if (d2.ConstructorArguments.Count == 1)
{
  var mode = d2.ConstructorArguments[0].Value as DebuggableAttribute.DebuggingModes;
  // Parse the modes enumeration and figure out the values.
}
else
{
  var tracking = (bool)d2.ConstructorArguments[0].Value;
  var optimized = !((bool)d2.ConstructorArguments[1].Value);
}

最後に、NamedArgumentsたとえば次を使用して、コンストラクターで設定されたものをオーバーライドする可能性があるかどうかを確認する必要があります。

var arg = NamedArguments.SingleOrDefault(x => x.MemberInfo.Name.Equals("IsJITOptimizerDisabled"));
var optimized = (arg == null || !((bool)arg.TypedValue.Value));

DebuggingModes最後に、これを.NET 2.0以降で実行していて、まだ見たことがない場合、MSDNはドキュメントでこれを指摘しています。

.NET Frameworkバージョン2.0では、JIT追跡情報は常に生成されます。このフラグは、IsJITTrackingEnabledプロパティがfalseである点を除いて、デフォルトと同じ効果があります。これは、バージョン2.0では意味がありません。

于 2012-05-03T21:51:42.247 に答える
11

を使用する必要がありますAssembly.ReflectionOnlyLoad

使用方法を示すMSDNノートを次に示します。

using System;
using System.IO;
using System.Reflection;

public class ReflectionOnlyLoadTest
{
    public ReflectionOnlyLoadTest(String rootAssembly) {
        m_rootAssembly = rootAssembly;
    }

    public static void Main(String[] args)
    {
        if (args.Length != 1) {
            Console.WriteLine("Usage: Test assemblyPath");
            return;
        }

        try {
            ReflectionOnlyLoadTest rolt = new ReflectionOnlyLoadTest(args[0]);
            rolt.Run();
        }

        catch (Exception e) {
            Console.WriteLine("Exception: {0}!!!", e.Message);
        }
    }

    internal void Run() {
        AppDomain curDomain = AppDomain.CurrentDomain;
        curDomain.ReflectionOnlyPreBindAssemblyResolve += new ResolveEventHandler(MyReflectionOnlyResolveEventHandler);
        Assembly asm = Assembly.ReflectionOnlyLoadFrom(m_rootAssembly);

        // force loading all the dependencies
        Type[] types = asm.GetTypes();

        // show reflection only assemblies in current appdomain
        Console.WriteLine("------------- Inspection Context --------------");
        foreach (Assembly a in curDomain.ReflectionOnlyGetAssemblies())
        {
            Console.WriteLine("Assembly Location: {0}", a.Location);
            Console.WriteLine("Assembly Name: {0}", a.FullName);
            Console.WriteLine();
        }
    }

    private Assembly MyReflectionOnlyResolveEventHandler(object sender, ResolveEventArgs args) {
        AssemblyName name = new AssemblyName(args.Name);
        String asmToCheck = Path.GetDirectoryName(m_rootAssembly) + "\\" + name.Name + ".dll";
        if (File.Exists(asmToCheck)) {
            return Assembly.ReflectionOnlyLoadFrom(asmToCheck);
        }
        return Assembly.ReflectionOnlyLoad(args.Name);
    }

    private String m_rootAssembly;
}
于 2012-05-03T21:58:32.433 に答える
5

現在のAppDomainでアセンブリをアンロードすることはできません。これは、残念ながら.NETが機能するように設計されている方法です。これは、ReflectionOnlyロードの場合にも当てはまります。また、これを行うには少ししわがあり、通常のGetCustomAttributesの代わりにGetCustomAttributesDataメソッドを使用する必要があります。後者は、属性コンストラクターでコードを実行する必要があるためです。これは人生をより困難にする可能性があります。

動作するはずの代替手段は、通常の意味で実際にロードせずにアセンブリを検査できるCecilを使用することです。しかし、それは多くの余分な作業です。

于 2012-05-03T22:02:19.967 に答える
1

Assembly.ReflectionOnlyLoadがあなたが探しているものだと思います。

于 2012-05-03T21:52:23.180 に答える