visual studio serial read open example ejemplo datareceived com1 c# .net serial-port

studio - uart c#



La clase SerialPort ocasionalmente se cuelga en Dispose (2)

He escrito una aplicación de consola .net 4.0 que periódicamente habla con un módem GSM para obtener una lista de los mensajes SMS recibidos (es un módem USB pero el código se conecta a él a través de un controlador de puerto serie y envía comandos AT; dicho sea de paso, un módem Sierra Wireless pero no puedo cambiarlo y tengo el último controlador). Lo que sucede es que después de un período de tiempo (tal vez horas, tal vez días) simplemente deja de funcionar. Aquí hay un fragmento de registro ...

2012-04-17 23:07:31 DEBUG Modem Check (108) - Executing AT command ''AT+CPMS="ME"''... 2012-04-17 23:07:31 DEBUG Modem Check (108) - Finished executing ''AT+CPMS="ME"'' 2012-04-17 23:07:31 DEBUG Modem Check (108) - Detaching event handlers for ''COM13'' 2012-04-17 23:07:31 DEBUG Modem Check (108) - Disposing the SerialPort for ''COM13''

Ese es el final del registro, nada más, aunque esperaría ver al menos una declaración más, aquí está el código relevante:

internal T Execute() { var modemPort = new SerialPort(); T ret; try { modemPort.ErrorReceived += ModemPortErrorReceived; modemPort.PortName = _descriptor.PortName; modemPort.Handshake = Handshake.None; modemPort.DataBits = 8; modemPort.StopBits = StopBits.One; modemPort.Parity = Parity.None; modemPort.ReadTimeout = ReadTimeout; modemPort.WriteTimeout = WriteTimeout; modemPort.NewLine = "/r/n"; modemPort.BaudRate = _descriptor.Baud; if (!modemPort.IsOpen) { modemPort.Open(); } ret = _command.Execute(modemPort, _logger); _logger.Debug("Detaching event handlers for ''{0}''", _descriptor.PortName); modemPort.ErrorReceived -= ModemPortErrorReceived; _logger.Debug("Disposing the SerialPort for ''{0}''", _descriptor.PortName); } catch (IOException ex) { _logger.Error(ex.Message); throw new CommandException( string.Format(CultureInfo.CurrentCulture, ModemWrapperStrings.COMMAND_ERROR, ex.Message), ex); } catch (UnauthorizedAccessException ex) { _logger.Error(ex.Message); throw new CommandException( string.Format(CultureInfo.CurrentCulture, ModemWrapperStrings.COMMAND_ERROR, ex.Message), ex); } finally { modemPort.Dispose(); _logger.Debug("Modem on port ''{0}'' disposed", _descriptor.PortName); } return ret; }

Como puede ver, se cuelga en el método Dispose de la clase SerialPort.

Hice algunas búsquedas en Google y llegué a esta cuestión: Cierre de puerto serie Cuelga la aplicación de este hilo: el puerto serie se cuelga mientras se cierra . Lo consensuado parece ser cerrar el puerto con un hilo diferente, ¿es eso solo para una aplicación de formularios? En mi caso, tengo una aplicación de consola simple, así que no creo que se aplique (solo se ejecuta en un bucle en el hilo principal). Ni siquiera estoy seguro de que este sea realmente el problema (mi sensación es que es más probable que haya un problema con el controlador del puerto serie del módem, pero no sé y quizás estoy siendo injusto con el módem). Por lo que yo veo, tengo tres opciones:

  1. Cierre el puerto en un hilo diferente
  2. Ponga un retraso antes de cerrar el puerto
  3. Deje el puerto abierto para siempre

Realmente no me gusta ninguna de esas soluciones, pero estoy pensando en dejar el puerto abierto y solo ver qué pasa (tengo la sensación de que perderá memoria o algo peor, expondrá algún otro problema con el módem, pero tal vez solo soy pesimista). y si ese es el caso, probablemente podría salirse con la suya cerrándolo cada 24 horas, por ejemplo, y volviendo a abrirlo de nuevo) así que mi pregunta es ...

¿Hay algún problema alternativo con este código que pueda estar causando esta falla o hay una solución alternativa a lo que he descrito anteriormente?


SerialPort es algo propenso a un punto muerto. Con mucho, la causa más común es la que encontró, se desencadena al usar Invoke () en un controlador de eventos DataReceived. Claramente no es tu caso aquí.

Estos interbloqueos están relacionados con un hilo de trabajo que SerialPort comienza detrás de la cortina. Ese hilo ayuda a detectar eventos asincrónicos en el puerto, el winapi nativo subyacente es WaitCommEvent (). Ese trabajador hace que los eventos DataReceived, PinChanged y ErrorReceived funcionen. Tenga en cuenta cómo se usa ErrorReceived.

El método Dispose () hace lo mismo que el método Close (), señala el hilo de trabajo para salir. Sin embargo, la falla es que no espera a que salga el hilo. Esa es una receta para problemas, del tipo que está explícitamente documentado en el artículo de MSDN para SerialPort.Close () en la sección Comentarios:

La mejor práctica para cualquier aplicación es esperar una cierta cantidad de tiempo después de llamar al método Close antes de intentar llamar al método Open, ya que es posible que el puerto no se cierre al instante.

Que es, francamente, la peor práctica posible para el consejo de "mejores prácticas", ya que no especifica en absoluto cuánto tiempo se espera que espere. Por una buena razón, no hay un valor seguro garantizado. Esperar un segundo o dos debería ser 99.9% bueno. El modo de falla 0.1% ocurre cuando la máquina está muy cargada y la cadena de trabajo simplemente no recibe suficientes ciclos para detectar la condición de cierre en el tiempo. Absolutamente undebuggable por supuesto.

Elimine este problema, solo abra un puerto serie al comienzo de su programa y ciérrelo al salir. En lugar de causar problemas, esto también garantiza que no pierda el acceso al puerto al azar cuando otro programa salta y le roba el puerto. Y tenga en cuenta que cerrar el puerto ya no es necesario, Windows se ocupará de eso si no lo hace.


Si está utilizando el evento DataRecibved o cualquier otro evento de su puerto serie, debe eliminar su controlador de eventos antes de deshacerse del puerto serie.

mySerial.DataReceived -= DataReceivedHandler; mySerial.Dispose();

El bloqueo ocurre porque tienes un evento que dispara sobre un objeto eliminado ... lo cual obviamente es un error.

Sin embargo, en tu caso lo has hecho ... y el problema es que el puerto no se ha cerrado. posiblemente un thread.sleep podría permitir que el puerto se "estabilice" antes de intentar reabrirlo. Probablemente también sea específico del hardware ... por lo que no hay una mejor práctica.

Lo mismo que para un control de formularios: cómo eliminar todos los controladores de eventos de un control