2
votes

I am trying to load an assembly (dll) into an AppDomain and call an entry point. (essentially bootstrap a package into an Azure environment) I have been following this SO article (How do I create an application domain and run my application in it?) and I think I am doing it right, but I'm having some issues.

I have used several articles on here to get me as far as I am, but I keep running into a FileNotFoundException as described in Unable to load executing assembly into new AppDomain, FileNotFoundException. My problem is that solution doesn't work. The assembly I am trying to execute exists in a different location. So ApplicationBase needs to be the folder of the assembly I am trying to execute.

var otherType = typeof(BootstrapProxy);
var domaininfo = new AppDomainSetup
    {
        ConfigurationFile = executingAssembly + ".config",
        ApplicationBase = _root
    };
Evidence adevidence = AppDomain.CurrentDomain.Evidence;
_domain = AppDomain.CreateDomain(_type.ToString(), adevidence, domaininfo);
_domain.AssemblyResolve += (sender, args) =>
    {
        var lookupPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        if (lookupPath == null) return null;
        var assemblyname = new AssemblyName(args.Name).Name;
        var assemblyFileName = Path.Combine(lookupPath, assemblyname + ".dll");
        var assembly = Assembly.LoadFrom(assemblyFileName);
        return assembly;
    };
var proxy = _domain.CreateInstanceAndUnwrap(otherType.Assembly.FullName, otherType.FullName) as BootstrapProxy;

The last line throws the exception and the AssemblyResolve event never fires off (as determined by placing a breakpoint on the var lookupPath line.

I have also tried the AppDomain.CurrentDomain.AssemblyResolve event with the same handler as above with no luck. I have also tried creating the same handler inside the BootstrapProxy class.

I think I am doing this properly but make note of the first paragraph, so if I'm completely off base, I'm not averse to doing things a different way.

UPDATE:

I have changed the code around to forcibly load the assemblies into the new appdomain and still have issues.

var otherType = typeof(BootstrapProxy);
var domaininfo = new AppDomainSetup
    {
        ConfigurationFile = executingAssembly + ".config",
        ApplicationBase = _root
    };
Evidence adevidence = AppDomain.CurrentDomain.Evidence;
_domain = AppDomain.CreateDomain(_type.ToString(), adevidence, domaininfo);
var deps = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
foreach (var dep in deps)
{
    var lookupPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    if (lookupPath == null) continue;
    var assemblyname = new AssemblyName(dep.Name).Name;
    var assemblyFileName = Path.Combine(lookupPath, assemblyname + ".dll");
    if (File.Exists(assemblyFileName))
        _domain.Load(File.ReadAllBytes(assemblyFileName));
}
_domain.Load(File.ReadAllBytes(Assembly.GetExecutingAssembly().Location));
var sl = _domain.GetAssemblies().ToArray();
var proxy = _domain.CreateInstanceAndUnwrap(otherType.Assembly.FullName, otherType.FullName) as BootstrapProxy;

sl shows that all the dlls, including the one referenced in the FileNotFoundException are loaded in the new appdomain.

public class BootstrapProxy : MarshalByRefObject
{
    public void Main()
    {
        Console.WriteLine("Magic happened.");
    }
}

UPDATE 2:

I Changed it around to this:

var deps = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
foreach (var dep in deps)
{
    var lookupPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    if (lookupPath == null) continue;
    var assemblyname = new AssemblyName(dep.Name).Name;
    var assemblyFileName = Path.Combine(lookupPath, assemblyname + ".dll");
    if (File.Exists(assemblyFileName))
        File.Copy(assemblyFileName, Path.Combine(_root, assemblyname + ".dll"));
}

File.Copy(Assembly.GetExecutingAssembly().Location, Path.Combine(_root, Path.GetFileName(Assembly.GetExecutingAssembly().Location)));
var otherType = typeof(BootstrapProxy);
var domaininfo = new AppDomainSetup
    {
        ConfigurationFile = executingAssembly + ".config",
        ApplicationBase = _root
    };
Evidence adevidence = AppDomain.CurrentDomain.Evidence;
_domain = AppDomain.CreateDomain(_type.ToString(), adevidence, domaininfo);
var proxy = _domain.CreateInstanceAndUnwrap(otherType.Assembly.FullName, otherType.FullName) as BootstrapProxy;
if (proxy != null)
{
    proxy.Main();
}

This method of copying the assembly and its references to the ApplicationBase of the new AppDomain is not ideal, as there are a few common references and I could end up with version conflicts and other issues.

1

1 Answers

1
votes

Just a guess, but the problem is here:

var otherType = typeof(BootstrapProxy);

By doing this, you are loading that assembly into the calling appdomain. Due to initialization, it attempts to load assembly not present in the calling domain's lookup path. KABOOM!

To solve this:

Refer to otherType by its fully qualified name and passing in the assembly name as a string too. (I think you might get away with just using the FQN of the type)

Also. You should not really handle AssemblyResolve for assemblies outside the appdomain.