【问题标题】:Freeing up Serial Port properly正确释放串口
【发布时间】:2010-12-06 09:46:10
【问题描述】:

我正在用 c# 编写一个应用程序,它利用 SerialPort 类与一些设备进行通信。现在我一直遇到的一个大问题是如何正确释放那里的资源,因为在尝试使用已被使用的串行端口时会立即出现异常。

由于通常 GC 应该处理大部分工作,我有点想不出还有什么可以尝试的...

主要是我尝试了两件事(在我的逻辑中)应该做的工作。我使用基于会话的通信,所以我在每次通信之前和之后调用OpenPortClosePort 方法 - 所以应该关闭端口。此外,我还尝试将包含端口的对象设置为空 - 但我仍然一直得到 UnauthorizedAccessExceptions - 尽管我 100% 确定 SerialPort.Close() 方法已被调用。

你们知道释放端口的更好方法,所以我不再遇到这种异常了吗?

编辑: 感谢您的回答,但 Dispose() 的东西不起作用 - 我之前曾尝试过 - 也许我做错了什么,所以这是我的代码的示例:

这实际上很像 Øyvind 建议的,虽然我只是添加了 IDisposable - 但也不起作用:

所以这将是我的包装类:

class clsRS232 : IDisposable
{
  public void clsRS232()
  {
    Serialport port = new Serialport("COM1",9600,Parity.none,8,Stopbits.one);
  }
  public void openPort()
  {
     port.Open();
  }
  public void sendfunc(string str)
  {
    port.Write(str);
  }
  public string readfunc()
  {
    port.ReadTo("\n");
  }

  public void Dispose()
  {
     port.Dispose();
  }

}

现在每当我需要 rs232 通信时,我都会像这样调用一个新实例:

   clsRS232 test = new clsRS232;
   test.openport();
   test.sendfunc("test");
   test.Dispose();

但这并没有改变任何东西 - 我仍然得到很多 UnauthorizedAccessExceptions - 如果另一个人是对的(SerialPort 类的 Dispose() 只包含 SerialPort.Close() ) - 那么我想我没有t 真的改变了我之前的方法,我有一个函数调用 close();

感谢您的回答 - 仍然希望找到解决方案 :)

【问题讨论】:

    标签: c# .net serial-port


    【解决方案1】:

    由于SerialPort 实现了IDisposable,您应该这样编写代码:

    using( SerialPort port = new SerialPort( ... ) ){
      //do what you need with the serial port here
    }
    

    这将确保在using 块的末尾,串行端口被释放,如果在using 块内发生异常,它会被释放,因为using 块与一个 try/finally 块,在 finally 块中关闭/处理 SerialPort

    编辑

    根据 OP 的需要,SerialPort 应保持打开的时间长于方法的时间范围。

    在这种情况下,我会将与串行端口有关的整个逻辑封装在它自己的类中。在类的构造函数中,打开串口,编写方法来做你需要的操作。然后让这个类自己实现IDisposable,并在你自己的Dispose 方法中配置SerialPort。

    这将使您更好地控制必须打开和关闭/处置串行端口的位置,并将串行端口逻辑包装到适当的类中。

    如果你想让端口在一段不容易被代码块包含的时间段内保持打开状态,那么你必须在完成它时手动释放它,例如当使用它的函数被关闭时或任何触发程序中 com 端口的释放。

    编辑 2

    你当前的实现是这样的:

    clsRS232 test = new clsRS232;
    test.openport();
    test.sendfunc("test");
    test.Dispose();
    

    这里的问题是,如果sendfunc 以某种方式导致异常,它永远不会被释放。首先从实现IDisposable 获得的好处是,您可以将代码更改为如下所示:

    using( clsRS232 test = new clsRS232 ){
     test.openport();
     test.sendfunc("test");
    }
    

    现在,无论using 块内是否有任何异常,都可以保证为您的com 端口调用Dispose

    【讨论】:

    • 感谢您的快速回答,但我不能那样做,因为我已经在一个自己的类中包装了很多自己的方法,我将其用于串行端口通信 - 他们都使用一个端口,这是在第一次调用类时初始化的 - 所以如果我想这样做,我最终不得不在每个使用端口的方法中编写一个这样的块,不是吗?
    • 是从创建类开始就保持端口打开,等所有操作完成后关闭端口,还是每次打开和关闭端口的连接操作?
    • 第一个 - 我的包装类通过它的构造函数启动一个 SerialPort 实例 - 然后可以通过多种方法使用它来发送/接收等,但是一旦我调用我自己的 close() 函数(目前包含 SerialPort.Close()) 我希望 Serialport 可以完全免费再次实例化。在每次操作之前和之后打开和关闭 ebe 都不起作用,因为例如我会在写了一些东西之后关闭端口并再次打开它以接收一些东西——>缓冲区会消失......
    • 查看我的编辑。我认为这是处理这类工作的好方法。我在串行设备会在不同场合向我发送数据的情况下做了同样的事情,所以我需要打开端口以在收到数据时读取缓冲区,但我需要确保端口在结束。
    • 我还在我的原始帖子中进行了编辑 - 我的程序实际上很像你描述的那样,但它似乎没有那样工作......请看一下,也许我在某些地方弄错了一块……
    【解决方案2】:

    我知道这已经很老了,但我刚刚遇到了同样的问题,这个解决方案对我有用,虽然它有点老套。根据此线程why is access to com port denied?,问题在于 SerialPortClass 中的错误。我创建了一个只打开一次端口的包装类,并在应用程序的生命周期内创建该类。然后将 SerialPort 置于类的 Dispose 中,但使用以下命令打开:

      private SerialPort KickerPort { get; set; }
        .
        .
        .
    private bool OpenPort()
            {
                //https://stackoverflow.com/questions/7219653/why-is-access-to-com-port-denied
                //due to a bug in the SerialPort code, the serial port needs time to dispose if we used this recently and then closed
                //therefore the "open" could fail, so put in a loop trying for a few times
                int sleepCount = 0;
                while (!TryOpenPort())
                {
                    System.Threading.Thread.Sleep(100);
                    sleepCount += 1;
                    System.Diagnostics.Debug.Print(sleepCount.ToString());
                    if (sleepCount > 50) //5 seconds should be heaps !!!
                    {
                        throw new Exception(String.Format("Failed to open kicker USB com port {0}", KickerPort.PortName));
                    }
                }
                return true;
            }
         private bool TryOpenPort()
                    {
                        if (!KickerPort.IsOpen)
                        {
                            try
                            {
                                KickerPort.Open();
                                return true;
                            }
                            catch (UnauthorizedAccessException)
                            {
                                return false;
                            }
                            catch (Exception ex)
                            {
                                throw ex;
                            }
    
                        }
                        return true;
                    }
    

    这是由 :

    调用的
     try
                {
                    if (OpenPort())
                    {
                        //do your thing here !
                    }
                    return false;
                }
                catch (Exception ex)
                {  
                    throw ex;
                }
    

    在我的测试中(我用它来打开 USB 启动器上的现金抽屉)我发现有时它会第一次打开,而其他时候它会经历大约 20 次睡眠循环,具体取决于最近的时间

    【讨论】:

      【解决方案3】:

      Øyvind Bråthen 提出的实现使用 .NET 中的 IDisposable 模式。在 using 块结束时,调用 SerialPort 实例的 Dispose 函数,将释放关联的非托管资源(即串口)

      当你想释放它时自己调用port.Dispose()。

      【讨论】:

      • 是的 - 之前尝试过 Dispose() - 遗憾的是它并没有改变任何关于我的异常的东西:/ 谢谢 :)
      猜你喜欢
      • 1970-01-01
      • 2011-05-07
      • 2016-06-24
      • 2014-12-17
      • 2011-06-08
      • 2011-06-01
      • 2011-09-09
      • 2010-12-30
      • 2015-11-18
      相关资源
      最近更新 更多