¿Cómo se realiza el bucle a través de los ensamblajes actualmente cargados?


Tengo una página de "diagnóstico" en mi ASP.NET aplicación que hace cosas como verificar la(s) conexión (es) de la base de datos, mostrar las configuraciones de aplicaciones y conexiones actuales, etc. Una sección de esta página muestra las versiones de Ensamblado de tipos importantes utilizados en todo, pero no pude averiguar cómo mostrar efectivamente las versiones de TODOS los ensamblados cargados.

¿Cuál es la forma más efectiva de calcular todos los actualmente referenciados y/o cargados Ensamblados en un ¿Aplicación. NET?

Nota: No estoy interesado en métodos basados en archivos, como iterar a través de *.dll en un directorio en particular. Estoy interesado en lo que la aplicación es realmente con ahora.

Author: starblue, 2008-12-20

2 answers

Este método de extensión obtiene todos los ensamblados referenciados, recursivamente, incluidos los ensamblados anidados.

Al usar ReflectionOnlyLoad, carga los ensamblados en un AppDomain separado, que tiene la ventaja de no interferir con el proceso JIT.

Notarás que también hay un MyGetMissingAssembliesRecursive. Puede usar esto para detectar cualquier ensamblado faltante al que se haga referencia, pero que no esté presente en el directorio actual por alguna razón. Esto es increíblemente útil cuando se usa MEF. La lista de devoluciones le dará tanto el ensamblaje que falta, y quién es el propietario (su padre).

/// <summary>
///     Intent: Get referenced assemblies, either recursively or flat. Not thread safe, if running in a multi
///     threaded environment must use locks.
/// </summary>
public static class GetReferencedAssemblies
{
    static void Demo()
    {
        var referencedAssemblies = Assembly.GetEntryAssembly().MyGetReferencedAssembliesRecursive();
        var missingAssemblies = Assembly.GetEntryAssembly().MyGetMissingAssembliesRecursive();
        // Can use this within a class.
        //var referencedAssemblies = this.MyGetReferencedAssembliesRecursive();
    }

    public class MissingAssembly
    {
        public MissingAssembly(string missingAssemblyName, string missingAssemblyNameParent)
        {
            MissingAssemblyName = missingAssemblyName;
            MissingAssemblyNameParent = missingAssemblyNameParent;
        }

        public string MissingAssemblyName { get; set; }
        public string MissingAssemblyNameParent { get; set; }
    }

    private static Dictionary<string, Assembly> _dependentAssemblyList;
    private static List<MissingAssembly> _missingAssemblyList;

    /// <summary>
    ///     Intent: Get assemblies referenced by entry assembly. Not recursive.
    /// </summary>
    public static List<string> MyGetReferencedAssembliesFlat(this Type type)
    {
        var results = type.Assembly.GetReferencedAssemblies();
        return results.Select(o => o.FullName).OrderBy(o => o).ToList();
    }

    /// <summary>
    ///     Intent: Get assemblies currently dependent on entry assembly. Recursive.
    /// </summary>
    public static Dictionary<string, Assembly> MyGetReferencedAssembliesRecursive(this Assembly assembly)
    {
        _dependentAssemblyList = new Dictionary<string, Assembly>();
        _missingAssemblyList = new List<MissingAssembly>();

        InternalGetDependentAssembliesRecursive(assembly);

        // Only include assemblies that we wrote ourselves (ignore ones from GAC).
        var keysToRemove = _dependentAssemblyList.Values.Where(
            o => o.GlobalAssemblyCache == true).ToList();

        foreach (var k in keysToRemove)
        {
            _dependentAssemblyList.Remove(k.FullName.MyToName());
        }

        return _dependentAssemblyList;
    }

    /// <summary>
    ///     Intent: Get missing assemblies.
    /// </summary>
    public static List<MissingAssembly> MyGetMissingAssembliesRecursive(this Assembly assembly)
    {
        _dependentAssemblyList = new Dictionary<string, Assembly>();
        _missingAssemblyList = new List<MissingAssembly>();
        InternalGetDependentAssembliesRecursive(assembly);

        return _missingAssemblyList;
    }

    /// <summary>
    ///     Intent: Internal recursive class to get all dependent assemblies, and all dependent assemblies of
    ///     dependent assemblies, etc.
    /// </summary>
    private static void InternalGetDependentAssembliesRecursive(Assembly assembly)
    {
        // Load assemblies with newest versions first. Omitting the ordering results in false positives on
        // _missingAssemblyList.
        var referencedAssemblies = assembly.GetReferencedAssemblies()
            .OrderByDescending(o => o.Version);

        foreach (var r in referencedAssemblies)
        {
            if (String.IsNullOrEmpty(assembly.FullName))
            {
                continue;
            }

            if (_dependentAssemblyList.ContainsKey(r.FullName.MyToName()) == false)
            {
                try
                {
                    var a = Assembly.ReflectionOnlyLoad(r.FullName);
                    _dependentAssemblyList[a.FullName.MyToName()] = a;
                    InternalGetDependentAssembliesRecursive(a);
                }
                catch (Exception ex)
                {
                    _missingAssemblyList.Add(new MissingAssembly(r.FullName.Split(',')[0], assembly.FullName.MyToName()));
                }
            }
        }
    }

    private static string MyToName(this string fullName)
    {
        return fullName.Split(',')[0];
    }
}

Actualizar

Para hacer seguro este hilo de código, ponga un lock alrededor de él. Actualmente no es seguro para subprocesos de forma predeterminada, ya que hace referencia a una variable global estática compartida para hacer su magia.

 17
Author: Contango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-12 21:35:50

Obteniendo ensamblados cargados para el AppDomain actual:

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

Obteniendo las asambleas referenciadas por otra asamblea:

var referencedAssemblies = someAssembly.GetReferencedAssemblies();

Tenga en cuenta que si el ensamblado A hace referencia al ensamblado B y al ensamblado A está cargado, eso no implica que el ensamblado B también esté cargado. El ensamblaje B solo se cargará cuando sea necesario. Por esa razón, GetReferencedAssemblies() devuelve AssemblyName instancias en lugar de Assembly instancias.

 183
Author: Kent Boogaart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-27 14:53:33