2

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.

Community
  • 1
  • 1
Pete Garafano
  • 4,863
  • 1
  • 23
  • 40

1 Answers1

1

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.

leppie
  • 115,091
  • 17
  • 196
  • 297
  • BootstrapProxy is present in the current domain since it is the assembly currently executing, isn't it? – Pete Garafano Jul 11 '13 at 14:16
  • @TheGreatCO: Yes, and because the way it loads, it will not cause `AssemblyResolve` to fire. (It is present in both domains I would guess). Trying hardcoding string parameters instead as a test, and it should work. `AssemblyResolve` should not even be needed. – leppie Jul 11 '13 at 14:17
  • @TheGreatCO: You seem to not follow. You should not be loading anything into the calling appdomain (unless I misunderstood what the code is doing). See an example here: https://github.com/leppie/IronScheme/blob/master/IronScheme/IronScheme/ExecutableTemplate.cs – leppie Jul 11 '13 at 14:29
  • My issue is the executing assembly resides on (for example) E:\AppRoot, and the assembly I wish to execute resides in C:\Resources. So (just like you) I set the AppDomain ApplicationBase to C:\Resources, but I am trying to load Bootstrapper.dll which resides in E:\AppRoot. SO the applicationbase of the currently executing domain and the new domain are not the same, or even close. – Pete Garafano Jul 11 '13 at 14:38
  • @TheGreatCO: I see you are using `BootstrapProxy` in the calling domain. How is that class defined? And for what purpose do you need it? – leppie Jul 11 '13 at 14:45
  • As stated at the top of the question, I am attempting to follow this example: http://stackoverflow.com/questions/2648411/how-do-i-create-an-appdomain-and-run-my-application-in-it It is entirely possible I'm doing it wrong. I updated the question with the class. – Pete Garafano Jul 11 '13 at 15:37
  • @TheGreatCO: It seems you are getting close :) Don't give up! Learning this stuff is very helpful. The fact that you still have to copy assemblies implies it is not really working as intended. As a tip, while debugging, you can see in the debug output window what assemblies are loaded into what appdomain. This might be helpful to debug the issue. – leppie Jul 11 '13 at 16:03
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/33292/discussion-between-thegreatco-and-leppie) – Pete Garafano Jul 11 '13 at 17:05