正确释放串行端口

时间:2010-12-06 09:46:10

标签: c# .net serial-port

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

由于通常情况下GC应该处理大部分工作,我有点想法还有什么可以尝试......

主要是我尝试了两件事(按照我的逻辑)应该做的工作。我使用基于会话的通信,因此我在每次通信之前和之后调用OpenPortClosePort方法 - 因此应该关闭端口。此外,我已经尝试将包含端口的对象设置为null - 但我仍然始终获得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()) - 阱那么我想我的天堂”我真的改变了我之前的方法,我有一个函数调用close();

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

3 个答案:

答案 0 :(得分:8)

由于SerialPort实现IDisposable,您应该编写如下代码:

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

这将确保在using块的末尾释放串行端口,并且如果在using块内发生异常则将其释放,因为using block与try / finally块完全相同,它关闭/处置finally块内的SerialPort

修改

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

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

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

如果您希望将端口保持打开一段时间不容易被代码块包含,那么您必须在完成后手动处理它,例如当使用它的函数关闭时或任何触发程序中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");
}

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

答案 1 :(得分:3)

我知道这是非常古老的,但我遇到了同样的问题,这个解决方案对我有用,虽然它有点hacky。根据这个线程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次,具体取决于最近的时间

答案 2 :(得分:1)

ØyvindBråthen提出的实现使用.NET中的IDisposable模式。在using块的末尾,调用SerialPort实例的Dispose函数,它将释放相关的非托管资源(即串口)

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