释放未插入的虚拟串行端口

Posted

技术标签:

【中文标题】释放未插入的虚拟串行端口【英文标题】:Releasing a unplugged virtual Serial Port 【发布时间】:2012-04-07 19:36:41 【问题描述】:

我在使用 USB 条形码扫描仪时遇到了一点问题。 我正在使用带有“SerialPort”类的扫描仪:

        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;

如果我在通过“SerialPort”类打开 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);
    

【问题讨论】:

拔掉设备后,你尝试关闭串口是怎么回事? 拔下设备后,我将中止所有线程,这些线程以任何方式属于设备;我关闭了 SerialPort 本身(它不会引发错误)并且我正在处理对象 用更多代码编辑问题 那个 dispose 覆盖看起来有点没必要。您是否在线程内永久运行某些东西以使其保持活力?当一个线程“代码用完”时,它会自行关闭。 什么时候一切都冻结了? CheckUsbDeviceAvailability, BarcodeScannerDeviceNotAvailableEvent 是在拔掉设备后调用的? 【参考方案1】:

串行端口可以追溯到计算的石器时代。这就是您插入 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 事件的线程池线程很难处理。当您无法诊断软件问题时,您往往会归咎于硬件。除了拔掉硬件外,您几乎无法硬件。馊主意。现在你有两个问题。

【讨论】:

好吧,所以我不必再寻找我这边的问题了。我现在对串行端口的理解更加清晰了,谢谢你;) 这是一个非常彻底的答案。这应该是此类问题的规范答案。 -1 表示“USB 已完全取代串行端口硬件。”作为一揽子声明。 过去 15 年没有看电脑背面,我马上给你 -1。 @HansPassant 完全不正确。例如,戴尔当前的 Optiplex 系列 (9020) 具有串行端口。【参考方案2】:

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

【讨论】:

我知道这是一个已有 2 年历史的答案...如果您还在那里,您(或其他任何人)愿意详细说明吗?

以上是关于释放未插入的虚拟串行端口的主要内容,如果未能解决你的问题,请参考以下文章

使用串行端口,我收到一个错误:尝试读取或写入受保护的内存

VMware虚拟机删除文件,空间不释放

防止apache释放80端口

UIViewController 未释放

使用 C# 通过“USB 虚拟串行端口”与 USB 设备通信?

如果未使用 CloseHandle 正确关闭,则重新打开串行端口失败