3

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.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
philmo
  • 205
  • 3
  • 7
  • This should be useful: http://social.msdn.microsoft.com/Forums/en-US/clr/thread/4d4dedc3-dc1c-411e-9c4c-29208942e834 – Hans Passant Feb 17 '11 at 21:46
  • Ok I decided to hack it hard and just solve this problem once and for all. This solution is nowhere near elegant, but it works. Basically, everytime I enter a new name, I copy the dll over with a different name, load it up, and use it. At the end, I try and delete them (if they are not locked up). Solves my problem for now until I become a better programmer. – philmo Feb 17 '11 at 22:25

3 Answers3

3

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.

flq
  • 22,247
  • 8
  • 55
  • 77
  • I will check you link and report back. Cheers – philmo Feb 17 '11 at 21:21
  • My main concern is being able to write some code while A is running and then have A execute the new code. Would this work with your method? If not, am I even trying to do this the right way or should I be considering something else? – philmo Feb 17 '11 at 21:32
0

The main problem is where you do:

Type type = assm.GetType("TestClient." + typeName);

This is happening in App A's main AppDomain, and the consequence is that the main AppDomain locks App B's assembly .dll

The link in flq's answer to his blog post should work for you.

Chris Wenham
  • 23,679
  • 13
  • 59
  • 69
  • I have updated the code but with no success. Any ideas why? I don't think I am calling anything in the main AppDomain, but I could obviously be mistaken. – philmo Feb 17 '11 at 21:25
0

The type has to be loaded into the main appdomain as soon as you unwrap the ObjectHandle. To work properly, you need to operate on the non-unwrapped ObjectHandle.

erikkallen
  • 33,800
  • 13
  • 85
  • 120
  • No it doesn't. Unwrap() returns a proxy to the object in the other AppDomain, and so doesn't (necessarily) require assembly that defines the type to be loaded into the main AppDomain. – Iridium Feb 18 '11 at 08:34