2010-12-06 41 views
5

Estoy escribiendo una aplicación en C# que utiliza la clase SerialPort para comunicarse con algunos dispositivos. Ahora, el gran problema que he estado encontrando todo el tiempo es cómo liberar recursos de manera adecuada allí, ya que de inmediato se obtiene una excepción cuando se intenta utilizar un puerto en serie que ya se está utilizando.Liberando el puerto serie correctamente

Dado que normalmente la GC debe hacerse cargo de la mayor parte del trabajo que estoy un poco fuera de las ideas que más tratan ...

Principalmente me trataron 2 cosas que (en mi lógica) debe hacer el trabajo. Utilizo la comunicación basada en sesión, por lo que llamo a un OpenPort y un ClosePort método antes y después de cada comunicación, por lo que el puerto debe estar cerrado. También intenté configurar mi objeto que contiene el puerto para que sea nulo después, pero igual recibo UnauthorizedAccessExceptions todo el tiempo, aunque estoy 100 por ciento seguro de que se ha llamado al método SerialPort.Close().

¿Ustedes conocen alguna forma mejor de liberar los puertos, así que dejo de obtener esa excepción?

EDIT: Gracias por las respuestas, pero las cosas Dispose() no está funcionando - Me habían intentado antes - tal vez estoy haciendo algo mal, aunque por lo que aquí está un ejemplo lo que parece mi código gusto:

En realidad es bastante como Øyvind sugirió, aunque acabo de agregar el IDisposable - pero no funciona bien:

lo que esta sería mi clase de contenedor:

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(); 
    } 

} 

Ahora cada vez que necesito de comunicación RS232 I Cal La nueva instancia de esta manera:

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

Pero eso no cambia nada - me dan un montón de UnauthorizedAccessExceptions - y si el otro tenía razón (que Dispose() de la clase SerialPort sólo contiene SerialPort.Close ()) - Bueno, entonces supongo que realmente no he cambiado nada desde mi enfoque anterior, donde tuve una llamada a función close();

gracias por sus respuestas - todavía con la esperanza de encontrar la solución :)

Respuesta

6

Desde SerialPortIDisposable implementos, se debe escribir el código como el siguiente:

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

Esto se asegurará de que al final de el bloque using, se libera el puerto serie, y si se produce una excepción dentro del bloque using se libera, ya que el bloque using es exactamente igual que un bloque try/finally, que cierra/elimina el SerialPort dentro del bloque finally.

EDITAR

De acuerdo con las necesidades de la OP SerialPort debe permanecer abierto más largo que el plazo de un método.

En ese caso envolvería toda la lógica que tiene que ver con el puerto serie dentro de su propia clase. En el constructor de la clase, abra el puerto serie y escriba los métodos para realizar las operaciones que necesita. Luego haga que esta clase implemente IDisposable y deseche SerialPort dentro de su propio método Dispose.

Esto le dará un mejor control sobre dónde tiene que abrir y cerrar/disponer el puerto serie y ajustar la lógica del puerto serie en una clase adecuada.

Si desea mantener el puerto abierto durante un período de tiempo que no se contiene fácilmente por un bloque de código, tendrá que desecharlo manualmente cuando termine de usarlo, por ejemplo, cuando la función que lo utiliza se pone cerrado o lo que sea que desencadena el lanzamiento del puerto de com en su programa.

EDIT 2

Su implementación actual es la siguiente:

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

El problema aquí es que si sendfunc provoca una excepción de alguna manera, nunca desecharse. Lo que se gana a partir de la implementación de IDisposable, en primer lugar, es que se puede cambiar el código para tener este aspecto:

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

Ahora se le garantiza que Dispose serán llamados para su puerto com con independencia de las excepciones dentro de la using bloquear.

+0

Gracias por la respuesta rápida, pero realmente no puedo hacerlo de esa manera, ya que he envuelto una gran cantidad de métodos propios en una clase propia, que utilizo para la comunicación serialport - todos usan el único puerto, que se inicializa cuando se llama por primera vez a la clase, así que si quisiera hacerlo de esta manera terminaría teniendo que escribir un bloque como este en todos los métodos que usan el puerto, ¿no? – Lorenz

+0

¿Desea mantener el puerto abierto desde el momento en que crea la clase y cerrarlo cuando finalizan todas las operaciones, o desea abrir y cerrar la conexión al puerto antes y después de cada operación? –

+0

el primero - mi clase contenedora inicia una instancia de SerialPort a través de su constructor - esto puede usarse a través de múltiples métodos para enviar/recibir, etc. pero una vez que llamo a mi propia función close() (que en este momento contiene SerialPort.Close()) Quiero que Serialport sea completamente libre para ser instanciado de nuevo. Abrir y cerrar antes y después de cada operación no funcionaría ya que, por ejemplo, habría cerrado el puerto luego de escribir algo y abrirlo de nuevo para recibir algo -> el buffer se habría ido ... – Lorenz

0

La implementación propuesta por Øyvind Bråthen utiliza el patrón IDisposable en .NET. Al final del bloque de uso, se llama a la función Dispose de la instancia de SerialPort, que liberará los recursos no administrados asociados (es decir, el puerto serie)

Puerto de llamada. Desembale() usted mismo cuando desee liberarlo.

+0

sí - había intentado Dispose() antes - por desgracia, no cambia nada con respecto a mis excepciones:/Gracias :) – Lorenz

2

Sé que esto es muy viejo, pero me encontré con el mismo problema y esta solución funcionó para mí, aunque es un poco hacky. De acuerdo con este hilo why is access to com port denied? el problema es con un error en SerialPortClass. Creé una clase contenedora que solo abre el puerto una vez y creo la clase durante toda la vida de la aplicación. El SerialPort está dispuesto a continuación en el Desechar de la clase, pero se abre utilizando la siguiente:

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; 
       } 

esto es llamado por:

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

en mis pruebas (lo utilicé para abrir un cajón de dinero en un USB kicker) Descubrí que a veces se abrió la primera vez y otras veces pasó por el ciclo de sueño unas 20 veces, dependiendo de cómo recientemente

Cuestiones relacionadas