How to Load an Assembly to AppDomain with all references recursively?

abatishchev picture abatishchev · Mar 18, 2009 · Viewed 134.9k times · Source

I want to load to a new AppDomain some assembly which has a complex references tree (MyDll.dll -> Microsoft.Office.Interop.Excel.dll -> Microsoft.Vbe.Interop.dll -> Office.dll -> stdole.dll)

As far as I understood, when an assembly is being loaded to AppDomain, its references would not be loaded automatically, and I have to load them manually. So when I do:

string dir = @"SomePath"; // different from AppDomain.CurrentDomain.BaseDirectory
string path = System.IO.Path.Combine(dir, "MyDll.dll");

AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
setup.ApplicationBase = dir;
AppDomain domain = AppDomain.CreateDomain("SomeAppDomain", null, setup);

domain.Load(AssemblyName.GetAssemblyName(path));

and got FileNotFoundException:

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

I think the key part is one of its dependencies.

Ok, I do next before domain.Load(AssemblyName.GetAssemblyName(path));

foreach (AssemblyName refAsmName in Assembly.ReflectionOnlyLoadFrom(path).GetReferencedAssemblies())
{
    domain.Load(refAsmName);
}

But got FileNotFoundException again, on another (referenced) assembly.

How to load all references recursively?

Do I have to create references tree before loading root assembly? How to get an assembly's references without loading it?

Answer

Jduv picture Jduv · Nov 13, 2012

You need to invoke CreateInstanceAndUnwrap before your proxy object will execute in the foreign application domain.

 class Program
{
    static void Main(string[] args)
    {
        AppDomainSetup domaininfo = new AppDomainSetup();
        domaininfo.ApplicationBase = System.Environment.CurrentDirectory;
        Evidence adevidence = AppDomain.CurrentDomain.Evidence;
        AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);

        Type type = typeof(Proxy);
        var value = (Proxy)domain.CreateInstanceAndUnwrap(
            type.Assembly.FullName,
            type.FullName);

        var assembly = value.GetAssembly(args[0]);
        // AppDomain.Unload(domain);
    }
}

public class Proxy : MarshalByRefObject
{
    public Assembly GetAssembly(string assemblyPath)
    {
        try
        {
            return Assembly.LoadFile(assemblyPath);
        }
        catch (Exception)
        {
            return null;
            // throw new InvalidOperationException(ex);
        }
    }
}

Also, note that if you use LoadFrom you'll likely get a FileNotFound exception because the Assembly resolver will attempt to find the assembly you're loading in the GAC or the current application's bin folder. Use LoadFile to load an arbitrary assembly file instead--but note that if you do this you'll need to load any dependencies yourself.