0

I want to load and unload an assembly using AppDomain and it does not work. Here the simplest example I came up with.

using System;
using System.Reflection;

namespace DomainTest
{
    class Program
    {
        static void Main(string[] args)
        {
            AppDomain _newDomain;
            var setup = new AppDomainSetup();
            setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
            setup.ApplicationName = "Isolator";

            _newDomain = AppDomain.CreateDomain("Isolation:" + Guid.NewGuid(),
               null, setup);

            Console.WriteLine("-----------------Before Domain created");
            ShowLoadedAssemblies();

            _newDomain.Load("WorkerTest");
            Console.WriteLine("---------------------After Load");
            ShowLoadedAssemblies();

            AppDomain.Unload(_newDomain);
            Console.WriteLine("--------------------After Unload");
            ShowLoadedAssemblies();

            Console.ReadLine();

        }

        public static void ShowLoadedAssemblies()
        {
            AppDomain currentDomain = AppDomain.CurrentDomain;
            //Make an array for the list of assemblies.
            Assembly[] assems = currentDomain.GetAssemblies();

            //List the assemblies in the current application domain.
            Console.WriteLine("Currently loaded Assemblies:");
            foreach (Assembly assem in assems)
                Console.WriteLine(assem.ToString());
        }
    }
}

For the simplicity of the example the WorkerTest assembly is an empty assembly with no references to anything except standard Microsoft.CSharp and System.Dll

namespace WorkerTest
{
    public class Worker
    {

    }
}

This example has a Program class with Main() method where I create a new AppDomain (_newDomain) and load a WorkerTest.DLL assembly into it. After that I unload the _newDomain and expect the WorkerTest.DLL to be unloaded with it, but it does not happen. Below you can see the program output that shows all the loaded assemblies before the load, after the load of WorkerTest into the _newDomain and after the _newDomain is unloaded. The WorkerTest.DLL is still loaded. Could someone tell me why it does not work?

 -----------------Before Domain created
Currently loaded Assemblies:
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Microsoft.VisualStudio.HostingProcess.Utilities, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Microsoft.VisualStudio.HostingProcess.Utilities.Sync, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Microsoft.VisualStudio.Debugger.Runtime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
vshost32, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
DomainTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
---------------------After Load
Currently loaded Assemblies:
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Microsoft.VisualStudio.HostingProcess.Utilities, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Microsoft.VisualStudio.HostingProcess.Utilities.Sync, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Microsoft.VisualStudio.Debugger.Runtime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
vshost32, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
DomainTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
WorkerTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
--------------------After Unload
Currently loaded Assemblies:
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Microsoft.VisualStudio.HostingProcess.Utilities, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Microsoft.VisualStudio.HostingProcess.Utilities.Sync, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Microsoft.VisualStudio.Debugger.Runtime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
vshost32, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
DomainTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
WorkerTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
  • take a look at this posting if it helps - http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm do a google search on the following `C# load and unload an assembly using AppDomain` – MethodMan Sep 01 '16 at 19:19

1 Answers1

0

I believe the problem is with this _newDomain.Load("WorkerTest"); which returns Assembly object to current app domain. Since Assembly includes access to all meta info it has to actually load assembly to current domain too.

To properly load assembly into new domain it should be done by code in that new domain alone. Or if you just need to create instance of an object - Loading DLLs into a separate AppDomain

Community
  • 1
  • 1
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • I looked for a way to load the assembly inside the new domain alone as you suggested, but could not find any examples on how to do that. Could you point me to the right direction? – Simongold Sep 02 '16 at 17:36
  • http://stackoverflow.com/questions/2008691/pass-and-execute-delegate-in-separate-appdomain, basically you need to make sure `Assembly.Load` happens inside new App domain and you never return types from that assembly back to original domain. – Alexei Levenkov Sep 02 '16 at 18:57
  • I tried the example you suggested and it did not work for me. The problem is that in order to specify Assembly FullName and Type Full Name in CreateInstanceAndUnwrap method I need to reference the assembly in the parent domain. And at the moment whan I call this CreateInstanceAndUnwrap method the parent domain loads this assembly. I even created a third assembly just to hold the Interface definition and it also did not help. What am I missing here? – Simongold Sep 03 '16 at 00:03
  • Why? The code that will load other assembly should be in your `Program` class (or sibling of it) - it is definitely safe to load it to both appdomains (and it is for sure loaded into current appdomain :) ) – Alexei Levenkov Sep 03 '16 at 00:08
  • May be we don't understand each other. The code that loads the other assembly using the proxy is in my Program class. In order to call this method: WorkerTest.IWorker proxy = (WorkerTest.IWorker)_newDomain.CreateInstanceAndUnwrap( typeof(WorkerTest.Worker).Assembly.FullName, typeof(WorkerTest.Worker).FullName); I have to reference in the parent domain the Assembly that I want to run in the child domain. This reference causes the Assembly to be loaded into the parent domain and it is not unloaded when I unload the child domain. The file that has this assembly is still locked. – Simongold Sep 03 '16 at 00:39
  • You probably should ask separate question... But I'm not sure how you managed to load other assembly into your main AppDomain if WorkerTest is in main assembly and Assembly.Load is presumably in WorkerTest.DoWork() call that you remotely call from main assembly on instance from child assembly (as shown in question I linked earlier - "// your code here")... If `WorkerTest` is in the other assembly indeed you get into trouble, but why would you do that ? – Alexei Levenkov Sep 03 '16 at 00:47
  • When I carefuly read the article you suggested to the very end (that I should've done from the beginning) I found out that what I observed is what it should be: "As is coded in the example, the parent appdomain will actually end up loading both the IRuntime and Runtime assemblys but that is because in the call to CreateInstanceAndUnwrap I am using typeof(Runtime) to get the assembly name and fully qualified type name. You could instead hardcode or retrieve these strings from a file - which would decouple the dependency." I went this 'file' route and now I am all set. Спасибо! – Simongold Sep 03 '16 at 23:16