0

I'm using an AppDomain to dynamically load/unload a dll called Logic.dll, which is in the Foo subfolder of my app. Here's my code:

public static class Wrapper
{
    private static AppDomain domain = null;

    public static Assembly MyResolveEventHandler(object sender, ResolveEventArgs e)
    {
        return Assembly.LoadFrom(e.Name);
    }

    public static void Initialize()
    {
        if (domain != null)
        {
            AppDomain.Unload(domain);
        }

        try
        {
            string path = Environment.CurrentDirectory + "\\Foo\\";

            AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
            setup.PrivateBinPath = "Foo";                
            domain = AppDomain.CreateDomain("MyDomain", null, setup);
            domain.AssemblyResolve += MyResolveEventHandler;

            DirectoryInfo info = new DirectoryInfo(path);

            foreach (FileInfo file in info.GetFiles("*.dll"))
            {
                string[] filePathStrings = file.FullName.Split('\\');
                string fileName = filePathStrings[filePathStrings.Length - 1];
                if (fileName.Split('.')[0] == "Logic")
                {
                    domain.Load(fileName.Replace(".dll", ""));
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }            
    }        
}

When I run this code, I get a FileNotFound exception: Could not load file or assembly 'Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

Using fuslogvw.exe to debug, I'm finding that the AppDomain's private path is null, even though I set it in the code:

=== Pre-bind state information ===
LOG: DisplayName = Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
 (Fully-specified)
LOG: Appbase = file:///C:/Program Files/MyApp/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = MyApp
Calling assembly : (Unknown).
===

Is there a reason why the appDomain private path is being nullified?

P.S. I can't use the app.config to change the Probing value, since that causes other problems that I can't get around.

  • `setup.PrivateBinPath = "Foo"` looks suspicious... normally you'd use absolute path for such parameter... Also you need to figure out which domain throws exception - default (which is my guess) or new one... – Alexei Levenkov Aug 14 '17 at 22:09
  • `AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;` I wouldn't do that -- `AppDomainSetup` is a `class`, so you're taking the original AppDomain's setup (a reference object) and then modify it after the fact. Just create a new `AppDomainSetup`. Copy over properties if necessary, though it shouldn't be. – Jeroen Mostert Aug 14 '17 at 22:22
  • @Alexei The default domain is throwing the exception - and since I can't change the app.config to look in Foo it's not finding the file, which makes sense. But ultimately I don't want the dll to be loaded in the default appDomain anyway, just in the new appDomain. Do you know if there's a way to do that? – bemailloux Aug 14 '17 at 22:23
  • @JeroenMostert Makes sense, I only need the PrivateBinPath to be set. – bemailloux Aug 14 '17 at 22:27
  • Something like https://stackoverflow.com/questions/658498/how-to-load-an-assembly-to-appdomain-with-all-references-recursively . You need to be *very* careful not to "leak" references to loaded assembly outside of new AppDomain as when default appdomain sees any type from that assembly it had to load assembly into default domain first. – Alexei Levenkov Aug 14 '17 at 22:34
  • So basically I needed to create a class (inheriting from `MarshalByRefObject`) and use `AppDomain.CreateInstanceAndUnwrap()` to create an instance of that class in the new AppDomain. Then I could load the assembly in the new AppDomain without it trying to load in the default domain. Thanks for your help @Alexei! – bemailloux Aug 15 '17 at 18:35

1 Answers1

0

Based on @Alexei Levenkov's comment:

You need to be very careful not to "leak" references to loaded assembly outside of new AppDomain as when default appdomain sees any type from that assembly it had to load assembly into default domain first.

If you try to load the dll straight into the remote AppDomain using AppDomain.Load or Assembly.LoadFrom, the assembly has to be loaded into the default AppDomain first. Since the default AppDomain wasn't looking anywhere other than the default PrivatePath, it was showing as NULL in the log.

To get around this, you have to load the assembly using an object that exists only in the remote AppDomain. This answer has an example of an object that loads an assembly to a remote AppDomain: https://stackoverflow.com/a/13355702/8464121