6

Apologies for the dodgy question - happy to rephrase if someone has a better suggestion.

I'm trying to create an object by dynamically invoking an assembly belonging to another application.

The following PowerShell code is working nicely for me:

[Reflection.Assembly]::LoadFrom("C:\Program Files\Vendor\Product\ProductAPI.dll")
$bobject = new-object ProductAPI.BasicObject    
$bobject.AddName("Some Name") 

I'm struggling to do the same thing in C#. Based on other posts on StackOverflow I currently have this:

System.Reflection.Assembly myDllAssembly =
System.Reflection.Assembly.LoadFile("C:\\Program Files\\Vendor\\Product\\ProductAPI.dll");

System.Type BasicObjectType = myDllAssembly.GetType("ProductAPI.BasicObject");

var basicObjectInstance = Activator.CreateInstance(BasicObjectType);

The final line results in a TargetInvocationException.

{"Could not load file or assembly 'AnotherObject, Version=1.2.345.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."

It appears that the BasicObject constructor is trying to invoke AnotherObject (from AnotherObject.dll in the same folder) but can't find it.

Any tips on how to get around this?

nathanchere
  • 8,008
  • 15
  • 65
  • 86
gf131072
  • 155
  • 2
  • 5
  • I think the DLL you're loading requires another DLL to work. You can explore the dependencies of a DLL using this tool. http://www.dependencywalker.com/ – Reactgular Sep 22 '13 at 10:31
  • When you find the missing DLL. Either copy it to your release folder or modify the windows PATH to include it. – Reactgular Sep 22 '13 at 10:32
  • Worth checking what's failing to load with [`FUSLOGVW.exe`](http://msdn.microsoft.com/en-us/library/e74a18c4.aspx) (*Assembly Binding Log Viewer*) which will show you what is failing to be loaded (and where the .NET loader is looking). – Richard Sep 22 '13 at 10:47
  • 1
    @MathewFoscarini The .NET assembly loader does not directly use the PATH (now does the native dll loader either: or at least not directly). Better to consider *[How the Runtime Locates Assemblies](http://msdn.microsoft.com/en-us/library/yx7xezcf.aspx)*. Notably the location of the assembly loaded via `LoadFrom` is including when loading any dependences. See my previous comment: first thing to do is know what is failing to be found. – Richard Sep 22 '13 at 10:49
  • 1
    Thanks guys - it does look like I just failed to realise that the executable pretty much needs to be in the same folder as the application I'm looking to work with. – gf131072 Sep 22 '13 at 11:04
  • I know where AnotherObject.dll is -it's in the folder containing ProductAPI.dll. Is there any way that my executable can live outside of this folder but tell the runtime to add this to the list of locations it should search when given a partial assembly path (i.e. just the name), at least for the life of my application? – gf131072 Sep 22 '13 at 11:08

1 Answers1

8

If it can't find a dependent assembly in the usual places, you'll need to manually specify how to find them.

The two easiest ways I'm aware of for doing this:

  1. manually load the dependent assemblies in advance with Assembly.Load.

  2. handle the AssemblyResolve event for the domain which is loading the assembly with additional assembly dependencies.

Both essentially require you to know the dependencies for the assembly you're trying to load in advance but I don't think that's such a big ask.

If you go with the first option, it would also be worthwhile looking into the difference between a full Load and a reflection-only Load.

If you would rather go with 2 (which I'd recommend), you can try something like this which has the added benefit of working with nested dependency chains (eg MyLib.dll references LocalStorage.dll references Raven.Client.dll references NewtonSoft.Json.dll) and will additionally give you information about what dependencies it can't find:

AppDomain.CurrentDomain.AssemblyResolve += (sender,args) => {

    // Change this to wherever the additional dependencies are located    
    var dllPath = @"C:\Program Files\Vendor\Product\lib";

    var assemblyPath = Path.Combine(dllPath,args.Name.Split(',').First() + ".dll");

    if(!File.Exists(assemblyPath))
       throw new ReflectionTypeLoadException(new[] {args.GetType()},
           new[] {new FileNotFoundException(assemblyPath) });

    return Assembly.LoadFrom(assemblyPath);
};
nathanchere
  • 8,008
  • 15
  • 65
  • 86