I am having trouble figuring something out about my AppDomain.Unload(...)
call. I have a detailed explanation with code from my earlier question. As it turns out, I was performing a couple of steps that apparently, I don't need to. However, I am fairly certain that when an AppDomain is created and then held in a collection:
private static Dictionary<string , AppDomain> HostDomains;
void StartNewDomain(string domainName)
{
AppDomain domain = AppDomain.CreateDomain(domainName);
HostDomains[domainName] = domain;
}
...when you are done with it, you must unload it:
if (HostDomains.ContainsKey(domainName))
{
AppDomain.Unload(HostDomains[domainName]);
HostDomains.Remove(domainName);
}
then remove domain from the collection.
When I unload the domain, however, the entire application is ending. If I remove the unload, all is well...and we are simply left with removing the domain from the collection. But I fear that my child AppDomain is not really unloaded. It may eventually get GC'd I guess, but that doesn't give me a warm fuzzy.
The child AppDomain assembly (a Windows Form application) is started asynchronously via an interface (IModule) that is referenced in my adapter class which inherits MarshalByRefObject
. I am wondering if this reference to IModule's Start() (which the plugin module assembly implements) is not marshaling properly (because of my implementation). So, when the Shutdown() method is called, the entire application dies. Should I make my IModule an abstract class instead so it should inherit MBR as well? Puzzled...
After looking at my code:
// instances the module for access to the module's Start() method
IModule module = (IModule)domain.CreateInstanceAndUnwrap(
ModuleManager.Modules[modName].Name,
ModuleManager.Modules[modName].EntryPoint.FullName);
...my fear is that since IModule is an interface, even though I am creating an instance in a child domain, the assembly is leaking into my main AppDomain. Therefore, when I attempt to unload the child domain, both domains are being unloaded. Would this be correct? And what would likely be the best solution to provide Start() & Stop() methods via the MBR (adapter) object?
UPDATE: see my answer below for changes --
Okay, there is no leaking -- everything inherits MBR:
- Host : MarshalByRefObject -- instances the ModuleAdapter in a new AppDomain
- ModuleAdapter : MarshalByRefObject -- IModule interface, interface methods (Start,Stop)
- MyModulePlugin : MarshalByRefObject -- Application.Run(myForm)
Am I doing something wrong still? I've tried several things and it just seems to be wrong or incomplete. When I tell the ModuleAdapter to shutdown, it calls AppDomain.Unload(AppDomain.CurrentDomain)
and the Host domain stops as well. I am still getting some first chance exceptions on the application exit. But the form (myForm) has been told to .Close().
So, I am still looking for the correct way of doing this...