9
votes

How can I create an appdomain, add assemblies to it, then destroy that app domain? This is what I have tried:

    static void Main(string[] args)
    {           
        string pathToExe = @"A:\Users\Tono\Desktop\ConsoleApplication1.exe";

        AppDomain myDomain = AppDomain.CreateDomain("MyDomain");

        Assembly a = Assembly.Load(System.IO.File.ReadAllBytes(pathToExe));

        myDomain.Load(a.FullName); // Crashes here!            
    }

I have also tried:

myDomain.Load(File.ReadAllBytes(pathToExe)); 

how can I add an assembly to the appdomain. Once I do that I can find the method via reflection execute it and then destroy the appdomain

The exception that I get is:

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

1
It is easier to load assembly to your own domain - so instead of trying to push assembly into other domain consider running some code in other domain to load whatever assembly you want (don't forget that you may need to pre-load dependencies or setup assembly search path correctly). - Alexei Levenkov
"Or one of its dependencies" is the usual problem. Use Fuslogvw.exe if you have no idea what that might be. And use AppDomainSetup to set the correct private path. - Hans Passant

1 Answers

11
votes

Two quick points:

  1. AppDomain.Load loads an assembly in the current AppDomain and not on myDomain (weird, I know).
  2. AppDomain.Load loads an assembly in the Load context, which resolves assemblies from the apps base-dir, the private-bin-paths and the GAC. Most probably, the assembly you try to load is not located in any of these locations which explains the exception message.

For more info have a look at this answer.

One way to load assemblies to an AppDomain is by creating a MarshalByRef-derived loader. You need something like this to avoid leaking types (and assemblies) to your main AppDomain. Here's a simple one:

public class SimpleAssemblyLoader : MarshalByRefObject
{
    public void Load(string path)
    {
        ValidatePath(path);

        Assembly.Load(path);
    }

    public void LoadFrom(string path)
    {
        ValidatePath(path);

        Assembly.LoadFrom(path);
    }

    private void ValidatePath(string path)
    {
        if (path == null) throw new ArgumentNullException("path");
        if (!System.IO.File.Exists(path))
            throw new ArgumentException(String.Format("path \"{0}\" does not exist", path));
    }
}

And use it like that:

//Create the loader (a proxy).
var assemblyLoader =  (SimpleAssemblyLoader)myDomain.CreateInstanceAndUnwrap(typeof(SimpleAssemblyLoader).Assembly.FullName, typeof(SimpleAssemblyLoader).FullName);
//Load an assembly in the LoadFrom context. Note that the Load context will
//not work unless you correctly set the AppDomain base-dir and private-bin-paths.
assemblyLoader.LoadFrom(pathToExe);

//Do whatever you want to do.

//Finally unload the AppDomain.
AppDomain.Unload(myDomain);