1
votes

I'm trying to work with AppDomains for the first time and I'm finding myself a bit lost.

Here is what I've done: I have a console app that instantiates a Bootstrapper class and calls Bootstrapper.Bootstrap.

The class looks something like this:

    public class Bootstrapper : MarshalByRefObject
    {
        private static AppDomain SecondaryAppDomain;
        private Bootstrapper _secondaryDomainBootstrapper;
        public Robot CurrentlyRunningRobot;
        public Bootstrapper OwningBootstrapper;

        public Bootstrapper()
        {

        }

        public void Bootstrap()
        {
            InitializeSecondaryAppDomain();
            RunInSecondaryAppDomain();
        }

        private void DestroySecondaryAppDomain()
        {
            AppDomain.Unload(SecondaryAppDomain);
        }

        private static int initCount = 0;

        private static void InitializeSecondaryAppDomain()
        {
            initCount++;
            SecondaryAppDomain = AppDomain.CreateDomain("SecondaryAppDomain" + initCount);
        }

        private void RunInSecondaryAppDomain()
        {
            _secondaryDomainBootstrapper =
                (Bootstrapper)
                    SecondaryAppDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName,
                        "myNamespace.Bootstrapper");
            _secondaryDomainBootstrapper.OwningBootstrapper = this;
            _secondaryDomainBootstrapper.Initialize(Args);
        }        

        private void Initialize(string[] args)
        {
            //Do some stuff...
            //Start() returns Task<Robot>
            var robot = Initializer.Start();
            CurrentlyRunningRobot = robot.Result;
            CurrentlyRunningRobot.HardResetRequested += OnHardResetRequested;
            robot.Wait();
        }

        private void DoHardReset()
        {
            DestroySecondaryAppDomain();
            InitializeSecondaryAppDomain();
            RunInSecondaryAppDomain();
        }

        private void OnHardResetRequested(object sender, EventArgs e)
        {
            OwningBootstrapper.DoHardReset();
        }
    }

The intention is that whatever is running in the Secondary domain and request that it be terminated and restarted.

What's happening, though, is that when I call DestroySecondaryAppDomain() (from within the default AppDomain) I wind up with ThreadAbortExceptions.

I've been reading a bunch of docs and that seems totally normal. The thing that I'm having difficulty with is why I can't seem to deal with it in my default AppDomain.

When the secondary AppDomain is unloaded (in DestroySecondaryAppDomain) I never get to execute the rest of the code in DoHardReset. What (probably simple) thing am I failing to understand?

1
If something is Serializable, it means the assembly will cross the appdomain boundary and you will not be able to unload it if a proxy class is not used. Edit: Normally a MarshalByRefObject class will implement an independent interface (which can be loaded in both appdomains). - leppie
Thanks. I don't remember what I was doing when I added that, but it doesn't seem to be necessary so I've removed it. Q: The independent interface--does it usually exist in a separate assembly? - Anthony Compton
Yes, the interface is in an independent assembly. - leppie
I don't see any exception handling/tracing around unload. Most likely you are getting AppdomainUnloadException (again expected if your second AppDomain does not shutdown in timely manner). - Alexei Levenkov
It's actually a ThreadAbortException. That's one of the things I don't understand--why do I get a ThreadAbortException cropping up in my default AppDomain? I would expect it to occur in the secondary AppDomain, but I sort of don't expect it to cross the boundary back into the default AppDomain. - Anthony Compton

1 Answers

1
votes

To make a long story short, there was still code executing in that AppDomain. It needed to be completely stopped before it can be successfully unloaded without error.