正在释放已拔出的虚拟串行端口



我在使用USB条形码扫描仪时遇到了一个小问题。我正在使用"串行端口"类的扫描仪:

        this._barcodeScanner = new SerialPort(comPort, 9600, Parity.None, 8, StopBits.One) { Handshake = Handshake.None, ReadTimeout = 500, WriteTimeout = 500 };
        this._barcodeScanner.Open();
        this._barcodeScanner.DataReceived += BarcodeScannerCallback;

如果我在USB设备通过"串行端口"类打开时拔下它,我将无法正确关闭软件,虚拟端口将永远保持打开状态,或者直到我重新启动整个计算机。

所以我的问题是,在我通过C#代码拔下设备后,有没有办法关闭虚拟端口?

问候

[编辑#1]

好吧,还有一些代码:

通过这种方式,我每10秒检查一次设备是否已插入:

    private bool CheckUsbDeviceAvailability()
    {
        ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\WMI",
        "SELECT * FROM MSSerial_PortName WHERE PortName = '" + this.PortName + "'");
        if (searcher.Get().Count > 0)
            return true;
        return false;
    }

这是串行端口的回调事件:

void BarcodeScannerCallback(object sender, SerialDataReceivedEventArgs e)
    {
        Thread.Sleep(500);
        string data = this._barcodeScanner.ReadExisting().Replace(Convert.ToChar(2), Convert.ToChar(32)).Trim();
        if (data.StartsWith("AX"))
        {
            string[] arrData = data.Split('n');
            this._barcodeScanner.StopAvailabilityThread();
            Barcode code = new Barcode(arrData[0].Replace("r", ""));
            if (CheckIfBarcodeExists(code))
                this.UpdateBarcodeNode(code);
            else
                this.CreateBarcodeNode(code);
            BarcodeScannerCallbackEvent(sender, e, code);
            this._barcodeScanner.StartAvailabilityThread();
        }
        this._barcodeScanner.ComDevicePluggedIn = ScannerDevice.ComAvailabilityState.Available;
    }

如果它不再应答,它将触发"DeviceNotAvailableEvent()":

    void BarcodeScannerDeviceNotAvailableEvent()
    {
        this._barcodeScanner.Close();
        this._barcodeScanner.Dispose();
    }

我已经覆盖了"SerialPort"类的Dispose事件,因此它将中止线程:

protected override void Dispose(bool isDisposing)
    {
        if (isDisposing)
        {
            this._deviceAvailableThread.Abort();
        }
        base.Dispose(isDisposing);
    }

串行端口可以追溯到计算的石器时代。这就是您插入ASR-33电传打字机开始在Fortran程序中打字的地方。电气接口非常简单。Windows API使用自己代码中的串行端口也是如此。实际上,任何运行时环境都支持它们。

USB已经完全取代了串行端口硬件。它有一个更高级的机器逻辑接口,支持许多不同类型的设备。它支持即插即用,允许操作系统检测设备何时连接或卸下,以及自动安装设备驱动程序等。

这种灵活性是有代价的。然而,USB设备总是需要一个设备驱动程序才能使用。设备驱动程序不是创建的。不同的驱动程序需要不同的方式与设备通话。通常通过DeviceIoControl()或Read/WriteFile()完成,但这些都是非常不透明的API函数。在USB的早期,设备制造商会提供一个DLL,该DLL提供了丰富的API来隐藏实现细节。

这并没有起到很好的作用,制造商不太擅长编写好的API,他们肯定不喜欢支持。因此,一个好的解决方案是支持一个标准的API,它可以在任何机器上使用,由任何运行时支持,由其他人记录和维护。就像串行端口API。

这并没有起到很好的作用,制造商不太擅长编写模拟串行端口的设备驱动程序。API最大的问题是它不支持即插即用。在所有串行端口硬件都没有支持它的逻辑接口之后,它的核心支持丢失了。有一些支持检测设备是否通过DTR硬件握手线路连接,但没有任何的支持来检测端口是否不再存在。

断开USB设备是个问题。在理想的情况下,设备驱动程序中内置的模拟器只需假装串行端口仍然存在,直到设备上的最后一个句柄关闭。这将是合乎逻辑的实现,因为没有办法触发即插即用事件。出于某种奇怪的原因,这似乎很难实施。大多数USB驱动程序都采用糟糕的快捷方式,它们只是让设备消失,即使它在使用

这会对使用该设备的任何用户模式代码造成严重破坏。它通常被写为假设它是一个真正的串行端口,并且真正的串行口不会突然消失。至少要画出明亮的蓝色火花。出了什么问题是不可预测的,因为这取决于驱动程序如何响应已不存在的设备上的请求。SerialPort启动的工作线程中出现无法捕获的异常是一个常见的错误。听起来你的驱动程序真的搞错了,它在MJ_CLOSE驱动程序请求上生成了一个错误返回代码。对于一个驱动程序来说,这是一件合乎逻辑的事情,毕竟设备已经不在了,但从你的一端来说是无法解决的。你有一个把手,但你关不上。那是一条没有桨的小溪。

NET的每个主要版本都对SerialPort类进行了一个小补丁,以尽量减少痛苦。但微软所能做的有限,捕捉所有错误并假装它们没有发生,最终导致即使有一个好的驱动程序,也无法再提供好的诊断。

因此,实用的方法是:

  • 始终在Windows中使用"安全删除硬件"托盘图标
  • 使用最新版本的.NET
  • 联系供应商并要求更新驱动程序
  • 抛弃那些提供糟糕司机的小贩
  • 告诉你的用户,仅仅因为这是你用USB设备唯一能做的事情,拔下它并不能解决任何问题
  • 使关闭端口在UI中变得容易和可访问
  • 将USB连接器粘在端口上,这样就无法将其取下

第五颗子弹也是让程序员陷入困境的原因。编写串行端口代码并不容易,它是高度异步的,并且运行DataReceived事件的线程池线程很难处理。当你不能诊断出软件问题时,你往往会责怪硬件。除了拔掉硬件,你几乎无法使用硬件。坏主意。现在你有两个问题。

此问题存在于.Net 2、3、3.5中您可以使用框架4(问题不存在于.Net 4中)

最新更新