Stopping Application Loop in secondary AppDomain

2019-08-07 02:20发布

问题:

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:

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 :-)