Impossibile ottenere i tipi dall'assembly dopo ReflectionOnlyLoadFrom

Nov 07 2020

Ho consultato il codice sul sito web http://codeproject.com con il titolo "Caricamento di assembly da qualsiasi luogo in un nuovo dominio app" di Marius Bancila, ma ho testato l'errore come nell'immagine allegata, al momento, non so risolverlo, spero tu aiuti, grazie.

Codice collegamento https://www.codeproject.com/Articles/453778/Loading-Assemblies-from-Anywhere-into-a-New-AppDom#_articleTop

Test

public class Program
{
    [STAThread]
    public static void Main()
    {
        var project = @"D:\Github\BeyConsPlugin\BeyConsProject\bin\x64\Debug\BeyConsRevitProject.dll";//Path to assembly
        var manager = new AssemblyReflectionManager();
        var success = manager.LoadAssembly(project, Guid.NewGuid().ToString());
        if (success)
        {
            var result = manager.Reflect(project, (a) =>
            {
                return a.GetTypes();
            });
            Console.WriteLine(string.Join("\n", result.Select(x => x.Name)));
        }            
        Console.ReadKey();
        manager.UnloadAssembly(project);
    }       
}

Errore

Risposte

1 MickyD Nov 07 2020 at 10:16

Resolver assieme non impostato

Il codice di Marius ha un bug per AssemblyReflectionProxyquanto riguarda il fatto che l'Assembly Resolver non è impostato se chiami LoadAssemblydiversamente Reflect<>.

A seconda di come viene creato un dominio dell'app figlio, durante il caricamento degli assembly potrebbe avere accesso solo alla cartella come specificato durante la creazione. Se è necessario eseguire il probe dell'assembly altrove per l'assembly o le sue dipendenze, è necessario un risolutore di assembly. Quando .NET cerca un assembly per un dominio, chiamerà il gestore come specificato ReflectionOnlyAssemblyResolvenell'evento dell'assemblie . Se non viene specificato o se il risolutore non riesce a individuare l'assembly, verrà visualizzato un messaggio di errore e verrà generata un'eccezione di errore di caricamento.

Ti consiglio di cambiare il codice da:

public class AssemblyReflectionProxy : MarshalByRefObject
{
  private string _assemblyPath;

  public void LoadAssembly(String assemblyPath)
  {
     try
     {
        _assemblyPath = assemblyPath;
        Assembly.ReflectionOnlyLoadFrom(assemblyPath);
     }
     catch (FileNotFoundException)
     {
        // Continue loading assemblies even if an assembly 
        // cannot be loaded in the new AppDomain.
     }
  }

...per:

public class AssemblyReflectionProxy : MarshalByRefObject
{
  private string _assemblyPath;

  public void LoadAssembly(String assemblyPath)
  {
     try
     {
        AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve // <---- add me
             += OnReflectionOnlyResolve; 

        _assemblyPath = assemblyPath;
        Assembly.ReflectionOnlyLoadFrom(assemblyPath);
     }
     catch (FileNotFoundException)
     {
        // Continue loading assemblies even if an assembly 
        // cannot be loaded in the new AppDomain.
     }
  }

Puoi vedere questo nel codice originale di Sacha quello su cui Marius ha basato il suo.

Aggiungi disposizione per i percorsi di risoluzione

L'altro problema con il codice è che entrambi presumono che durante il caricamento di un assembly, tutti gli assembly dipendenti si trovino nella stessa cartella, cosa che potrebbe non essere sempre il caso.

Modificare AssemblyReflectionProxyper includere un elenco di percorsi in cui sondare:

public List<string> ResolvePaths { get; set; }

Quindi modificare OnReflectionOnlyResolve in modo che sia simile al seguente:

private Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
{
     Assembly loadedAssembly =
         AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies()
             .FirstOrDefault(
               asm => string.Equals(asm.FullName, args.Name,
                   StringComparison.OrdinalIgnoreCase));

     if (loadedAssembly != null)
     {
        return loadedAssembly;
     }

    foreach (var tryFolder in ResolvePaths)
    {
        var asmName = args.Name.Split(',');
        var assemblyPath = Path.Combine(tryFolder, asmName[0] + ".dll");

        if (!File.Exists(assemblyPath))
            return null;

         return Assembly.ReflectionOnlyLoadFrom(assemblyPath);
    }
}

Cosa c'è in un nome?

Entrambi gli articoli hanno trascurato di sottolineare la stampa fine durante l'utilizzo ReflectionOnlyLoad. Sebbene Sacha abbia almeno menzionato che il suo codice era per un "generatore di codice", non posso fare a meno di chiedermi che entrambi gli articoli con il loro " Loading Assemblies.... into a New AppDomain" siano forse in qualche modo soggetti a interpretazione.

Lo scopo di ReflectionOnlyLoadè proprio questo - per la riflessione . Se carichi un assembly tramite questo metodo, non puoi eseguire alcun codice al suo interno. Inoltre, e un po 'sorprendente in un primo momento alla maggior parte dei programmatori riflettore di montaggio tra cui me è che la chiamata GetCustomAttributessarà anche fallire (perché tenta di "istanziare" tipi nella assemblaggio).

Se stai scrivendo il tuo sistema di plug-in in cui ogni plug-in ha il proprio dominio app, i Assemblymetodi di riflessione e caricamento sono utili per le diverse fasi della pipeline di caricamento del sistema di plug-in:

  1. primo passaggio: utilizzare ReflectionOnlyLoadcome metodo per ispezionare il plug-in per vedere se è valido; forse si desidera eseguire alcuni controlli di sicurezza con la certezza che nessuno dei codici dei plug-in può essere eseguito durante questa fase
  2. secondo passaggio: dopo aver verificato il plug-in, è possibile Load/ LoadFroml'assembly ed eseguire il codice in modo sicuro