【问题标题】:Problem with SerialPort串行端口问题
【发布时间】:2011-03-14 21:43:14
【问题描述】:

我正在使用 SerialPort 与条形码阅读器进行通信(只读)。

我已经安装了驱动程序来操作阅读器,就好像它是通过 Com-port 连接的一样,尽管它是一个 USB 设备。插入设备后,列表中会再出现一个 Com-port。

问题如下。我初始化 SerialPort 对象以从条形码阅读器读取,但如果阅读器被拔下,我无法正确完成或处置 SerialPort 对象,因为它“附加”到的端口不再存在。

程序关闭时结果为 WinIOException。我不仅在使用 SerialPort 的代码中,而且在 program.cs 级别也无法捕捉到它。根据堆栈,在尝试完成和处置 SerialPort 对象后会抛出 WinIOException。

有什么想法可以在这种情况下正确操作吗?或者至少捕获异常?

我可以肯定的是,问题不在于这个特定的驱动程序;我从另一家制造商那里购买了一个条码阅读器(使用相同目的的驱动程序)——情况是一样的。

【问题讨论】:

    标签: c# usb serial-port


    【解决方案1】:

    我通过创建一个处理串行端口的单独进程解决了这个问题。拔下串行端口后,我重新启动该过程。现在我可以在不重新启动主应用程序的情况下重新连接到拔出的串行端口设备。

    TLDR;创建一个单独的进程。

    示例代码

    串口进程代码

    static void Main(string[] args)
    {
        using (var output = Console.OpenStandardOutput())
        using (var serialPort = new SerialPort(args[0], int.Parse(args[1])))
        {
            serialPort.Open();
            while (serialPort.IsOpen)
            {
                serialPort.BaseStream.CopyTo(output);
            }
        }
    }
    

    主要应用程序

    var options = new ProcessStartInfo()
    {
        FileName = "Serial Port Process name here",
        UseShellExecute = false,
        RedirectStandardOutput = true,
    };
    
    using(var process = Process.Start(options))
    {
        using (StreamReader reader = process.StandardOutput)
        {
            while (!process.HasExited)
            {
                string result = reader.ReadLine();
                Console.WriteLine(result);
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      我也有过同样的经历。虽然不建议这样做,但您可以拔下串行设备并将其插入实际的串行端口,通信将重新开始。 USB 串行端口不能以这种方式工作。在软件实例化虚拟串行端口之前,我还遇到了通过 SerialPort.GetPortNames() 方法无法将 USB 串行端口显示为可用端口的问题。

      【讨论】:

        【解决方案3】:

        在我的代码中,这是作为 BaseStream 上 Finalize() 方法的一部分发生的,由垃圾收集器调用。

        因此,如果继承了 .NET SerialPort 类并覆盖了 Open/Close,则可以执行以下操作:

        在公开赛期间,只需拨打GC.SuppressFinalize(Me.BaseStream)

        收盘期间,尝试拨打GC.ReRegisterForFinalize(Me.BaseStream)

        如果 USB 已被拔出,这将引发异常,抱怨访问 BaseStream。在调用GC 之前检查.IsOpen 属性,或者如果您不相信.IsOpen 每次都返回False,则将其包装在Try Catch 中...

        这样就解决了,你的应用程序会处理它被拉出的事实,并且在你关闭时它不会崩溃。

        我目前无法做到,如果重新插入,则重新打开端口,但至少除了“不要触摸”的标签之外,还有一些进展......

        【讨论】:

        • 我已经使用了这个答案,它对我有用。重新插入设备后,我也能够重新打开端口。我所做的是在调用 Close 后调用 Dispose,然后将变量设置为 null。然后我的代码继续尝试重新建立联系。每次,它都会分配一个新的SafeSerialPort 对象(我称之为从SerialPort 继承的类)并尝试连接。如果失败,它会调用 Close、Dispose,并再次将变量设置回 null。工作正常。但是,您使用的设备及其驱动程序的行为可能会有所不同。
        • @Chimpster 我最近遇到了这个问题并尝试了您的解决方法。它有时会起作用,有时我实际上会因蓝屏而崩溃(错误检查)。我放弃了,只是决定在拔下串行电缆时处理我的应用程序崩溃。有时不值得处理其他人的糟糕代码。 (USB转COM适配器驱动)
        【解决方案4】:

        好吧,坏消息。

        Panagiotis Kanavos 的解决方案没有帮助。问题依然存在。

        .Net 4.0 也无济于事。我已经安装了 VS2010 - 没有任何改变。仍然抛出未处理的异常。不幸的是,“在 USB 插槽旁边贴上一个标志:“使用时请勿移除!”似乎是唯一的决定......

        【讨论】:

        • 啊,世界又变得有意义了。你真的应该改变答案标记。
        【解决方案5】:

        唉,这是 USB 串行端口仿真器的老问题。串行端口是可以追溯到石器时代的设备。它们曾经被拧入公共汽车,在程序使用它时无法将它们移除而不会产生火花和滚滚浓烟。石器时代还包括缺乏任何类型的即插即用支持,因此程序可以检测到设备突然变得古怪。

        不幸的是,大多数模拟它们的糟糕设备驱动程序只是让它们消失,即使程序打开了端口。当 Windows 向其写入文件时,这与将闪存驱动器从插槽中拔出一样有效。有一个后台工作线程等待来自设备驱动程序的通知,以便它可以生成 DataReceived、ErrorReceived 和 PinChanged 事件。当设备突然消失时,该线程会心脏病发作。抓不到,这是一个由 SerialPort 类启动的线程,不能用 try/catch 包装。

        根据大众的需求,Microsoft 在 .NET 4.0 中对此做了一些处理。实际上不确定该版本中会发生什么。如果您卡在较早的版本上,唯一合理的做法是在 USB 插槽旁边贴一个标志:“使用时请勿移除!”这不可避免地会让人至少拔掉设备两次,看看会发生什么。之后他们会对此感到厌烦,然后让你平静下来。

        非常不合理的解决方法是一个 app.exe.config 文件包含这样的内容:

        <?xml version ="1.0"?>
        <configuration>
          <runtime>
            <legacyUnhandledExceptionPolicy enabled="1"/>
          </runtime>
        </configuration>
        

        不要使用它。

        【讨论】:

        • 他们真的在 4.0 中解决了这个问题吗?很快有一天,我应该自己测试一下。如果是这样,那我只能说时间了。该错误已经存在很长时间了。
        • 不,它在 4.0 中没有修复(或 4.5 出现),您只能通过“忽略”它来阻止异常终止应用程序(以及其他任何其他类似的异常)来源)。
        【解决方案6】:

        您可以从 SerialPort 继承并重写 Dispose() 方法来处理此类异常。你可以直接吞噬异常(Dispose 无论如何都不应该抛出)。

        如果您想记录异常或以其他方式处理它,您必须先检查 disposing 标志。如果为 false,则意味着 Dispose 已被 SerialPort 的析构函数调用,并且该对象已成为孤立对象。

        例如

            public class MySerialPort:SerialPort
            {
                protected override void Dispose(bool disposing)
                {
                    try
                    {
                        base.Dispose(disposing);
                    }
                    catch (Exception exc )
                    {                    
                        if (disposing)
                        {
                            //Log the error
                        }
                    }
        
                }
            }
        

        【讨论】:

        • 谢谢,我会尽快尝试。
        • Hans Passant .Net 3.5。在一个简单的测试应用程序中检查它。明天将签入一个主应用程序。
        • 刚刚检查了 SerialPort.Dispose 与 Reflector。它抛出它的原因是它在其内部 SerialStream 类上调用 Close,而后者又对 EscapeCommFunction 进行不安全调用,如果失败则抛出 IOException。
        猜你喜欢
        • 2015-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多