views:

186

answers:

1

First off, apologies for the length...

I have a Host/Plugin application akin to MAF. We are not using any of the System.Addin or associated namespaces as this is a custom plugin architecture with multiple AppDomains in play. The Host UI (user interface) is running in it's own application loop (AppDomain). When an item in a listview is double-clicked the following occurs:

private static void StartPeripheralModule(string modName)
{
    AppDomain domain = AppDomain.CreateDomain(modName);
    // add to appdomains collection
    HostDomains[modName] = domain;

    // 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);

    // instance the adapter (inherits MBR)
    module.Adapter = new ModuleAdapter(modName, module);  // also saves a ref. to the IModule object

    // publish events decorated with [Serializable]
    module.Adapter.ModuleStarted += new ModuleAdapter.ModuleStartEventHandler(Adapter_ModuleStarted);
    module.Adapter.ModuleStopped += new ModuleAdapter.ModuleStopEventHandler(Adapter_ModuleStopped);
    module.Adapter.ModuleFaulted += new ModuleAdapter.ModuleFaultEventHandler(Adapter_ModuleFaulted);

    // add to adapters collection
    HostAdapters[modName] = module.Adapter;

    // asynchronous startup
    Action startup = module.Start;
    startup.BeginInvoke(null , null);
}

In the module.Start():

[STAThread]
public void Start( )
{
    //  do Start
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    MdiForm = new UnitMDIForm();
    MdiForm.FormClosed += new FormClosedEventHandler(MdiForm_FormClosed);

    adapter.OnModuleStarted(new ModuleAdapter.ModuleStartEventArgs(adapter));

    Application.Run(MdiForm);
}

MdiForm_FormClosed simply tells the Host that the module plugin is being closed via the plugin's UI and to begin closing procedures on the AppDomain. The module plugin starts as expected and the event OnModuleStarted works fine. When the listview item is once again double-clicked the module should shut down:

public static void UnloadInstance(string modName)
{
    Action shutdown = HostAdapters[modName].Module.Shutdown;
    IAsyncResult iaRes = shutdown.BeginInvoke(null , null);

    while (!iaRes.IsCompleted)  // poll wait state
    {
        Thread.Sleep(250);
        hostListener.Write(".");
    }
}

Shutdown function in module plugin:

public void Shutdown( )
{
    if (MdiForm.InvokeRequired)
    {
        MdiForm.Invoke((MethodInvoker)delegate
        {
            MdiForm.FormClosed -= MdiForm_FormClosed;
            Application.Exit();
        });
    }
    else
    {
        MdiForm.FormClosed -= MdiForm_FormClosed;
        Application.Exit();
    }

    adapter.OnModuleStopped(new ModuleAdapter.ModuleStopEventArgs(adapter));
}

The reason the MdiForm.FormClosed event is unsubscribed to is to prevent double firing. As soon as the Application.Exit() I get 1 - 2 '.' (dots) from the polling mechanism and then:

A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll A first chance exception of type 'System.Threading.ThreadAbortException' occurred in UnitTestWinForm.dll An exception of type 'System.Threading.ThreadAbortException' occurred in UnitTestWinForm.dll but was not handled in user code

Needless to say, we never reach our OnModuleStopped event where we officially unload the AppDomain and remove it and the adapter from our collections. I am putting a try/catch block in now to see if I can get anything more from the errors. From what I understand, I am following the proper procedure in exiting the application message loop and then unloading the domain. This gives the module the ability to clean up it's resources, etc.

Can anyone tell me what I'm doing wrong and/or how I should do this differently?

+1  A: 

This may help...

In your app, subscribe to:

        AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException;
        Application.ThreadException += ApplicationThreadException;

(see the help, they may make things easier but other times in debug mode you'll want this off)

I don't think you should be calling Application.Exit() from the module, it will (as far as I know) shut down the whole app.

Perhaps look at how A tool like NUnit loads and unloads AppDomains - I have never tried unloading one....

PK :-)

Paul Kohler
@Paul: I'll try and see what happens. Since the module is running in it's own AppDomain, then Applicatin.Exit() should be fine, AFAIK.
dboarman
@Paul: It turns out that I could have a larger threading issue than I thought. But it doesn't make sense if this is the case because where it appears to be getting hung should be on the main thread of the main AppDomain. :/ Still working on this...
dboarman
Marking as the answer. I did some more testing and calling the AppDomain.Unload(...) is causing an issue. This will be a new question.
dboarman
Cool - well, as in it helped!!
Paul Kohler
@Paul: All in all it caused me to do some trimming. Apparently I am doing something wrong because AppDomain.Unload(..) on a child domain *should not* affect the main application's domain. :( Here: http://stackoverflow.com/questions/2294474/appdomain-unload-killing-parent-appdomain
dboarman
And my threading issues came because I was asynchronously attempting to stop the child application...yuck!!! No need to do that.
dboarman