释放未插入的虚拟串口

10

我在使用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);
    }

在我看来,这段代码中的dispose操作似乎会在处理SerialPort之前终止线程,这可能会导致未管理的资源残留。与其终止线程,不如尝试停止事件循环,然后让线程自然结束。 - Adam Houldsworth
你是在关闭软件的同时尝试关闭端口吗? - Renatas M.
不,还没有。我会在测试应用程序中立即尝试它。之后我告诉你结果。 - Robert Nagel
我刚刚尝试了另一个USB设备。并没有使用条码扫描器。使用那个设备时,它按照我想要的方式工作。因此,可能是扫描仪驱动程序出了问题。有没有一种方法可以强制释放正在忙碌中的虚拟COM设备? - Robert Nagel
什么是ManagementObjectSearcher类?我尝试实现你的代码,但我没有ManagementObjectSearcher类。我尝试导入System.Management,但没有成功。 - E_Blue
显示剩余12条评论
2个回答

27

串口起源于计算机的石器时代。在那里,您可以插入ASR-33电传打字机开始输入Fortran程序。电气接口非常简单。从您自己的代码中使用串口的Windows API也很简单。几乎任何运行时环境都支持它们。

USB已经完全取代了串口硬件。它具有更先进的逻辑接口,支持许多不同类型的设备。并且它支持即插即用,允许操作系统检测到设备何时被连接或移除以及自动安装设备驱动程序等等。

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

那并不太好用,制造商并不擅长编写好的API,他们当然也不喜欢为其提供支持。因此,一个好的解决方案是支持标准API,这个API在任何机器上都可用,由任何运行时支持,由其他人撰写和维护文档。例如,串口API。

那并不太好用,制造商并不擅长编写模拟串口的设备驱动程序。该API最大的问题是它没有任何Plug and Play支持。核心支持缺失,毕竟串口硬件没有逻辑接口来支持它。有一些通过DTR硬件握手线检测到设备连接的支持,但是没有任何支持可以检测到该端口不存在。

拔出USB设备是个问题。在理想情况下,驱动程序内置的模拟器应该一直假装串口还在,直到设备上的最后一个句柄被关闭。这将是逻辑实现,因为没有触发即插即用事件的方法。但由于某种奇怪的原因,似乎很难实现。大多数USB驱动程序采用简单粗暴的快捷方式,它们只是让设备消失了,甚至在使用中。

这会对任何使用设备的用户模式代码造成混乱。通常写法默认设备是真正的串口,而真正的串口不会突然消失,除非引起明显异常。出现问题的具体情况很难预测,因为它取决于驱动程序如何响应已经不存在的设备的请求。由SerialPort启动的工作线程中产生无法捕获的异常是一个常见问题。听起来你的驱动程序真的搞砸了,它在MJ_CLOSE驱动程序请求上生成错误返回代码。这对于驱动程序来说是一件合理的事情,毕竟设备已经不存在了,但从你的角度来看却是无法解决的。你有一个句柄,但无法关闭它。这就像没有桨的船被漂流。

.NET的每一个主要版本都对SerialPort类进行了小的修补,以尽量减少困扰。但微软所能做的是有限的,捕获所有错误并假装它们没有发生最终会导致提供不良诊断的类,即使有一个好的驱动程序也是如此。

因此,实际的方法有:

  • 始终在Windows中使用“安全删除硬件”托盘图标
  • 使用最新版本的.NET
  • 联系供应商并请求驱动程序更新
  • 抛弃提供劣质驱动程序的供应商
  • 告诉你的用户,仅仅因为这是USB设备唯一的操作,并不能解决任何问题。
  • 在你的UI中简化关闭端口的步骤
  • 将USB连接器粘在端口上,使其无法拆卸

第五个问题也是程序员经常遇到的麻烦。编写串口代码并不容易,因为它是高度异步的,而运行DataReceived事件的线程池线程很难处理。当你无法诊断软件问题时,你往往会把责任归咎于硬件。但是,你几乎没有什么办法可以对硬件进行调试,只能拔下它。这是一个坏主意。现在你有两个问题。


3
这是一个非常详尽的回答。这应该成为此类问题的权威答案。 - Shane Wealti
2
“USB已经完全取代了串口硬件。”这样的一般性陈述是不准确的。 - Mark Schultheiss
2
我会因为你在过去的15年里没有查看电脑后面而扣除你1分。 - Hans Passant
1
@HansPassant 完全是不正确的。例如,戴尔目前的Optiplex系列(9020)有串行端口。 - dbasnett
1
我同意Mark的观点。虽然新款笔记本电脑没有真正的串口,但有许多设备使用USB线连接,但在设备端是串行的。这个问题与新电脑是否有串口无关。软件工程师将会支持串口-USB虚拟COM端口数十年,不是因为笔记本电脑上的端口,而是因为设备上的端口。这些类型的答案很烦人,因为这将在未来几十年或直到世界上所有串行设备停止存在之前都是一个真正的问题。 - shawn1874
显示剩余5条评论

0

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


1
我知道这是两年前的回答... 如果你还在,请问你(或其他任何人)是否可以详细说明一下? - Marcello Romani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接