2012-05-03 6 views
10

He creado una pequeña aplicación para cargar de forma recursiva conjuntos en un directorio proporcionado y leer su colección de atributos personalizados. Principalmente solo para leer DebuggableAttribute para determinar la configuración de IsJITTrackingEnabled y IsJITOptimizerDisabled para determinar si el ensamblaje está optimizado para su lanzamiento.Obtener atributos de ensamblaje personalizados sin cargarlos en el dominio de aplicación actual

Mi código actual hace un Assembly.LoadFrom para transferir la ruta completa al ensamblaje y cargarlo. Luego hace un GetCustomAttributes en el ensamblado para obtener el atributo de depuración. El problema es que cada ensamblaje se carga en el dominio de aplicación actual. Entonces, si otra carpeta usa el mismo ensamblaje, solo usa la referencia cargada originalmente. Me gustaría poder cargar el ensamblaje, leer las propiedades que necesito y luego descargarlo. Intenté crear un nuevo dominio de aplicación y cargar los ensamblajes en él, luego descargué el borrador de la reunión en vano.

Sé que esto debe ser posible, pero estoy perdido. Cualquier ayuda sería muy apreciada. Estaré encantado de proporcionar cualquier otra información que pueda necesitar también.

Respuesta

17

La respuesta corta es que no, no hay forma de hacer lo que estás pidiendo.

La respuesta más larga es esta: existe un método de carga de ensamblaje especial, Assembly.ReflectionOnlyLoad(), que utiliza un contexto de carga "solo de reflexión". Esto le permite cargar ensamblajes que no se pueden ejecutar, pero que pueden leer sus metadatos.

En su caso (y, al parecer, en todos los casos de uso que pude encontrar) no es tan útil. No puede obtener atributos escritos de este tipo de ensamblaje, solo CustomAttributeData. Esa clase no proporciona ninguna buena forma de filtrar un atributo específico (lo mejor que pude encontrar fue convertirlo en una cadena y usar StartsWith("[System.Diagnostics.Debuggable");

Peor aún, una carga de solo reflexión no carga ninguna dependencia asambleas, pero que le obliga a hacerlo manualmente Eso hace que sea objetivamente peor que lo que está haciendo ahora;.. por lo menos ahora se obtiene la carga de dependencia de forma automática

(Además, mi respuesta anterior hace referencia al MEF ; Me equivoqué, parece que MEF incluye una tonelada completa de código de reflejo personalizado para que esto funcione.)

En última instancia, no se puede unl y un conjunto una vez que ha sido cargado. Es necesario descargar la totalidad dominio de aplicación, como se describe en this MSDN article.

ACTUALIZACIÓN:

como se ha señalado en los comentarios, yo era capaz de obtener la información de atributos que necesitaba a través de la carga de la reflexión de sólo (y una carga normal), pero la falta de metadatos de atributos mecanografiados hace que sea un dolor serio.

Si se carga en un contexto de montaje normal, se puede obtener la información que necesita bastante facilidad:

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

Si se carga en un contexto de reflexión-solamente, se llega a hacer algo de trabajo; debe averiguar la forma que tomó el constructor del atributo, conocer cuáles son los valores predeterminados y combinar esa información para obtener los valores finales de cada propiedad.A obtener la información que necesita de esta manera:

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

A partir de ahí, es necesario comprobar la ConstructorArguments para ver qué constructor se llama: this one con un argumento o this one con dos argumentos. A continuación, puede utilizar los valores de los parámetros adecuados para averiguar cuáles son los valores de las dos propiedades que le interesan habría tomado:

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); 
} 

Por último, es necesario comprobar si hay NamedArguments que podrían invalidar los establecidos en el constructor, utilizando, por ejemplo:

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

en una nota final, si está ejecutando esto bajo .NET 2.0 o superior, y todavía no lo ha visto en, puntos de MSDN esto en la documentación: DebuggingModes

En .NET Framework versión 2.0, la información de seguimiento JIT siempre se genera, y este indicador tiene el mismo efecto que el predeterminado, con la excepción de que la propiedad IsJITTrackingEnabled es falsa, lo que no tiene ningún significado en la versión 2.0.

+0

¡Buen punto sobre la descarga! –

+0

Gracias. Lo había intentado pero estaba teniendo dificultades para obtener IsJITTrackingEnabled e IsJITOptimizerDisabled a través del objeto CustomAttributeData. ¿Estoy haciendo eso incorrectamente? – RockyMountainHigh

+0

Bueno, pude hacer lo que quería pero también descubrí que 'ReflectionOnlyLoad' es básicamente inútil. No carga dependencias automáticamente - * usted tiene que cargarlas usted mismo *. (También descubrí que MEF ha implementado un contexto de reflejo personalizado, y parece que no usa esto). –

0

Creo que Assembly.ReflectionOnlyLoad es lo que estás buscando.

9

Necesita usar Assembly.ReflectionOnlyLoad.

Éstos son algunos MSDN Notes que muestra cómo usarlo:

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; 
} 
4

No es posible descargar cada vez una asamblea en el dominio de aplicación actual, es sólo la forma en .NET está diseñado para trabajar por desgracia. Este es incluso el caso con las cargas ReflectionOnly. También hay un poco de arrugas al hacerlo así como también necesitas usar el método GetCustomAttributesData en lugar de los GetCustomAttributes normales, ya que este último necesita ejecutar código en el constructor de atributos. Esto puede hacer la vida más difícil.

Una alternativa que debería funcionar es usar Cecil que le permite inspeccionar el conjunto sin cargarlo realmente en el sentido normal. Pero eso es mucho trabajo extra.

Cuestiones relacionadas