Search code examples
c#appdomainshadow-copyappdomainsetup

Help needed with unloading .DLL's from AppDomain - Still not working even with ShadowCopy


I am trying to do the following. App A is the "mother app". It stays open. App B is just a .DLL where I write some classes that are derived from an interface specified in App A.

Then, from App A, I will "import" classes from App B and run methods within them. I want to be able to dynamically change App B (change code and recompile) and use the new code in App A.

I have a post-compile command in App B that copies the new .DLL to the App A directory. App A creates a new AppDomain and uses ShadowCopying. I thought this would be sufficient, but when I try to recompile & copy App B's new .DLL, it says that the file is in use and can't be overwritten.

Here is the code I have as of now:

App A (TestServer in code):

namespace TestServer
{
    public interface IRunnable
    {
        void Run();        
    }

    class Program
    {        
        static void Main(string[] args)
        {
            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationName = "DemoApp";
            setup.ApplicationBase = Environment.CurrentDirectory;
            setup.ShadowCopyDirectories = Environment.CurrentDirectory;
            setup.ShadowCopyFiles = "true";
            int _domain = 1;

            while (true)
            {
                string typeName = Console.ReadLine();

            AppDomain appDomain = AppDomain.CreateDomain("DemoDomain" + _domain, null, setup);

            IRunnable runner = appDomain.CreateInstanceFromAndUnwrap("TestClient.dll", typeName) as IRunnable;

            runner.Run();

            AppDomain.Unload(appDomain);
            _domain++;
            }
        }   
    }   
}

App B (TestClient in code):

namespace TestClient
{    
    public class TestRunner : TestServer.IRunnable
    {
        public void Run()
        {
            Console.WriteLine("RUNNING");
        }
    }

    public class Bob : TestServer.IRunnable
    {
        public void Run()
        {
            Console.WriteLine("BOB");
        }
    }
}

I have read that if you use stuff from other app domains, those app domains could automatically load the .DLL or something along those lines. In this case, I fear that using the Interface is causing the base AppDomain to load the .DLL thereby locking it up.

How can I resolve this issue / is there a better setup??

NOTE: I have update my code and it still yields the same result.


Solution

  • Your code still runs in the mother AppDomain, as you're pulling in assemblies and types into there. Any code should run in the spawned domain. I've shown one way to set something like that up on my website : A simple way to start your code in a different AppDomain

    I'm not 100% sure on that but that's certainly one step you'll have to undertake

    Update

    In terms of the solution presented there, your instantiation of a runner would happen in an inheritor of DomainLifetimeHook. The infrastructure shown ensures that the Start and Stop methods run in the AppDomain created by the class AppDomainExpander. That Expander is the class that creates the new domain, hence your domain setup should go in the Create method of the domain.