停止辅助 AppDomain 中的应用程序循环

发布于 2024-08-22 07:01:33 字数 3254 浏览 5 评论 0原文

首先,对篇幅表示歉意...

我有一个类似于 MAF 的主机/插件应用程序。我们没有使用任何 System.Addin 或关联的命名空间,因为这是一个具有多个应用程序域的自定义插件架构。主机 UI(用户界面)在其自己的应用程序循环(AppDomain)中运行。当双击列表视图中的某个项目时,会发生以下情况:

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

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 只是告诉主机模块插件正在通过插件的 UI 并开始关闭 AppDomain 上的过程。模块插件按预期启动,并且事件 OnModuleStarted 工作正常。当再次双击列表视图项时,模块应该关闭:

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(".");
    }
}

模块插件中的关闭功能:

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

取消订阅 MdiForm.FormClosed 事件的原因是为了防止双重触发。一旦 Application.Exit() 我得到 1 - 2 '.' (点)来自轮询机制,然后:

mscorlib.dll 中发生了“System.Threading.ThreadAbortException”类型的第一次机会异常 UnitTestWinForm.dll 中发生“System.Threading.ThreadAbortException”类型的第一次机会异常 UnitTestWinForm.dll 中发生“System.Threading.ThreadAbortException”类型的异常,但未在用户代码中处理

不用说,我们从未到达我们正式卸载 AppDomain 并将其删除的 OnModuleStopped 事件我们系列中的适配器。我现在放入一个 try/catch 块,看看是否可以从错误中获得更多信息。据我了解,我遵循正确的程序退出应用程序消息循环,然后卸载域。这使模块能够清理其资源等。

谁能告诉我我做错了什么和/或我应该如何以不同的方式做到这一点?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

染墨丶若流云 2024-08-29 07:01:33

这可能会有所帮助...

在您的应用程序中,订阅:(

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

请参阅帮助,它们可能会使事情变得更容易,但其他时候在调试模式下您会希望关闭此功能)

我认为您不应该调用 Application.Exit()从模块中,它会(据我所知)关闭整个应用程序。

也许看看像 NUnit 这样的工具如何加载和卸载 AppDomains - 我从未尝试过卸载一个....

PK :-)

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文