【问题标题】:Unload child AppDomain that contains POS hardware causing CannotUnloadAppDomainException卸载包含导致 CannotUnloadAppDomainException 的 POS 硬件的子 AppDomain
【发布时间】:2013-03-20 10:55:33
【问题描述】:

我使用 .net 4.0 和 POS.net 1.12,我在新的子 AppDomain 中创建了一个硬件类,这样任何未处理的异常都不会杀死我的父 AppDomain。

我可以创建子 AppDomain 并对其进行调用没有问题。但是,如果我尝试卸载 AppDomain,则会收到异常“CannotUnloadAppDomainException”。

我已经用谷歌搜索了这个问题,当线程无法被杀死时,通常会发生异常。我实际上并没有在子类中创建任何新线程。

我设法查明了导致此错误的代码。如果我创建 POS 硬件类并且它只创建 POS 对象,那么它工作正常。但是,如果我在任何硬件上调用方法“Open()”,则卸载时会发生此异常。现在,在我尝试卸载之前,我关闭了所有硬件并确保清理代码被命中,所以我不确定问题是什么。

这里是创建和卸载 AppDomain 的代码:

AppDomain hardwareDomain = AppDomain.CreateDomain("Hardware domain");

IHardwareManager hardwareManager =
        (IHardwareManager)hardwareDomain.CreateInstanceFromAndUnwrap(typeof(OposHardwareManager).Assembly.Location,
                                                                     typeof(OposHardwareManager).FullName);

hardwareManager.StartupHardware();

hardwareManager.CloseDownHardware();
hardwareManager = null;

// **** causes exception
AppDomain.Unload(hardwareDomain);

这里是硬件类:

public class OposHardwareManager : MarshalByRefObject, IHardwareManager
{
    private PosExplorer _posExplorer;
    private PosPrinter _printer;

    public void StartupHardware()
    {
        // create the hardware explorer
        this._posExplorer = new PosExplorer();

        // create and enable the printer
        DeviceInfo printerInfo = this._posExplorer.GetDevice(DeviceType.PosPrinter);
        PosDevice printerDevice = this._posExplorer.CreateInstance(printerInfo);
        this._printer = (PosPrinter)printerDevice;

        // ***** this line here, if run, causes the exception on unload 
        this._printer.Open();
        this._printer.Claim(2000);
        this._printer.DeviceEnabled = true;
    }

    public void CloseDownHardware()
    {
        this._printer.Release();
        this._printer.Close();

        this._printer = null;
        this._posExplorer = null;
    }
}

有什么想法吗?

【问题讨论】:

    标签: c# .net appdomain


    【解决方案1】:

    如果您在创建域中引用了一个类型(就像您对 typeof(OposHardwareManager) 所做的那样),程序集也将加载到该域中。发生这种情况时,iow 类型越过域边界“向上”,创建的域将无法卸载。

    我建议您不要引用包含 OposHardwareManager 的程序集,而只需使用完整的限定名称创建它。这可能涉及一些重构。

    【讨论】:

    • 我尝试了您的建议,但出现了同样的问题。我认为这不会解决问题,因为只要我不调用“.Open()”,我的原始代码将使用该程序集卸载 AppDomain 没有问题
    • @eyeballpaul:调试时查看调试输出窗口。它会告诉你程序集是否进入了创建域。
    • 我不是 100% 确定你的意思
    • @eyeballpaul:当一个程序集被加载时,它会显示在调试窗口中以及它是在哪个应用程序域中加载的。它会在每个应用程序域中加载一次,在您的情况下,我怀疑它会在两个应用程序域中加载,这会阻止卸载。
    • 它在该视图中显示一次。尽管由于某种原因,它旁边的名称是“ConsoleApplication1.vshost.exe”,这将是父域名。
    【解决方案2】:

    AppDomain.Unload 的文档中指出:

    如果一个线程没有中止,例如因为它正在执行 非托管代码,或者因为它正在执行 finally 块,然后在 一段时间内会抛出 CannotUnloadAppDomainException 最初调用 Unload 的线程。

    这可能是你的情况。看看PosDevice的文档如何正确释放资源。

    另外请注意,由在新 AppDomain 中创建的线程引发的未处理异常会导致整个应用程序崩溃。在AppDomain.UnhandledException Event 的文档中指出:

    在 .NET Framework 1.0 和 1.1 版中,未处理的异常 发生在主应用程序线程以外的线程中的是 被运行时捕获,因此不会导致应用程序 终止。因此,UnhandledException 事件可能是 在应用程序终止的情况下引发。从 .NET 开始 框架版本 2.0,这是子进程中未处理异常的后盾 线程被删除,因为这种沉默的累积效应 故障包括性能下降、数据损坏和 锁定,所有这些都难以调试。

    【讨论】:

    • 我知道如何正确释放 POS 资源(至少从我阅读的内容来看),所以我仍然不确定为什么我无法卸载它。但是,是的,正如你提到的,在我的例子中,使用新的 AppDomain 并不能真正解决我试图解决的问题。所以我目前正在努力通过进程之间的远程处理!
    【解决方案3】:

    不确定这是否有帮助,但我在 Win 7 和 XP 上发现了同样的错误。在应用程序退出期间,应用程序崩溃并引发错误。但是错误来自报告查看器,所以在卸载表单时请确保调用

    reportviewer.Reset();

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-12
      • 1970-01-01
      • 2012-11-06
      • 2011-01-07
      • 1970-01-01
      相关资源
      最近更新 更多