Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GetExportedTypes() FileNotFoundException: Assembly couldn't be found

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.

like image 814
Steffen Winkler Avatar asked Sep 09 '11 16:09

Steffen Winkler


2 Answers

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;
}
like image 101
Steffen Winkler Avatar answered Oct 06 '22 02:10

Steffen Winkler


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.

like image 29
Josh Avatar answered Oct 06 '22 00:10

Josh