GetExportedTypes() FileNotFoundException: Assembly couldn't be found
Asked Answered
D

2

8

My task: Find all Forms (WindowsForm or WPF, doesn't matter) in a dll or exe file and return that list. In theory that works (meaning: if I've an assembly with a WPF or WindowsForm my code manages to get all Forms, TextBoxes, Labels etc. ). When it comes to "real" assemblies it fails. I get FileNotFound exceptions when calling GetExportedTypes() for every "custom" assembly (.NET assemblies are found, no problems there). I already use GetReferencedAssemblies() to load the referenced assemblies (Reflection.Assembly.LoadFrom) and yes it does work (all assemblies are found and loaded into the AppDomain) but it doesn't help.

I checked the version numbers (they match), I copied my executable and the assembly into one directory with all referenced assemblies, doesn't work.

Here is my code, maybe someone figures out what I'm (obviously) doing wrong:

foreach (AssemblyName reference in selectedAssembly.GetReferencedAssemblies())
{
      if (System.IO.File.Exists(
             System.IO.Path.GetDirectoryName(selectedAssembly.Location) + 
                @"\" + reference.Name + ".dll"))
      {
         System.Reflection.Assembly.LoadFrom(
            System.IO.Path.GetDirectoryName(selectedAssembly.Location) + 
               @"\" + reference.Name + ".dll");
      }
      else if (System.IO.File.Exists(@"C:\dll\" + reference.Name + ".dll"))
      {
         System.Reflection.Assembly.LoadFrom(@"C:\dll\" + reference.Name + ".dll");
      }
      else
      {
         System.Reflection.Assembly.ReflectionOnlyLoad(reference.FullName);
      }

      selectedAssembly.GetExportedTypes();       
}

at first check if the referenced dll exists in the directory where the assembly is, if not check if it exists in C:\dll and if it's not there try and use the GAC. it does work and I've no errors from there but as soon as I come to GetExportedTypes it fails with a FileNotFound exception on the first custom library.

*edit 1 what do I mean by "real assemblies": I mean assemblies which are more complex and have references to non-standard-.NET libraries/assemblies


Thanks for the hint to fuslogvw.exe Hans Passant but what do you mean by "with code like this"?


okay I used fuslogvw.exe and I get two exceptions for every single dll that is referenced by the "selectedAssembly". The first one says something like "The binding starts in LoadFrom-context The image owned by the system isn't searched in LoadFrom-Context"

the other logentry says that the dll referenced by the selectedAssembly couldn't be found and it tried to download it from application's base path and all directories below...but not from it's actual location...so, key question: how do I change the Load-context to LoadFrom? And why is .NET so stubborn on this? I mean the assemblies are loaded in the AppDomain, it shouldn't care about the actual location of the assembly.


okay problem solved. Here is the solution: http://ayende.com/blog/1376/solving-the-assembly-load-context-problem

I implemented that into my existing class (removed the static-keyword and put the body of the Init method into my method), compiled it and it worked.

Thanks for your help guys.

Derision answered 9/9, 2011 at 16:3 Comment(2)
What do you mean by "real assemblies"Conditional
Use fuslogvw.exe to troubleshoot this. It shows you which bindings failed, including those for dependent assemblies. The usual problem with code like this.Wilek
D
6

okay problem solved. Here is the solution: http://ayende.com/blog/1376/solving-the-assembly-load-context-problem

I implemented that into my existing class (removed the static-keyword and put the body of the Init method into my method), compiled it and it worked.

Thanks for your help guys.

just in case the website will someday be unavailable, here is the sourcecode from ayende

static Dictionary<string, Assembly>assemblies;   

public static void Init()
{

    assemblies = new Dictionary<string, Assembly>();

    AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);

    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{

    Assembly assembly = null;

    assemblies.TryGetValue(args.Name, out assembly);

    return assembly;

} 

static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{

    Assembly assembly = args.LoadedAssembly;
    assemblies[assembly.FullName] = assembly;
}
Derision answered 12/9, 2011 at 6:51 Comment(0)
F
2

I would recommend using Reflector to see which references you may not have loaded. For instance, you are only loading the referenced assemblies that the current assembly is looking at. Do you step down through each child to find their referenced assemblies as well? The FileNotFound error is probably pointing you in the direction of a type that is declared in another assembly that isn't loaded.

Freeland answered 9/9, 2011 at 16:53 Comment(2)
hmm, thanks for that. I'll rebuild that foreach loop as a recursive loop. But can that really be the problem? I mean the assemblies are loaded into the AppDomain without an error and I only call "GetExportedTypes" on the chosen assembly, not on all the other assemblies.Derision
sorry no, that wasn't the problem. The LoadContext was the problem ;).Derision

© 2022 - 2024 — McMap. All rights reserved.