Liberación de un puerto serie virtual desconectado

Tengo un pequeño problema con un USB Barcode Scanner. Estoy usando el escáner con la clase “SerialPort”:

this._barcodeScanner = new SerialPort(comPort, 9600, Parity.None, 8, StopBits.One) { Handshake = Handshake.None, ReadTimeout = 500, WriteTimeout = 500 }; this._barcodeScanner.Open(); this._barcodeScanner.DataReceived += BarcodeScannerCallback; 

Si desenchufo el dispositivo USB mientras está abierto a través de la clase “SerialPort”, no puedo cerrar el software correctamente y el puerto virtual permanece abierto por toda la eternidad o hasta que reinicie toda la computadora.

Entonces mi pregunta es, ¿hay alguna forma de cerrar el puerto virtual después de desconectar el dispositivo a través del código C #?

Saludos

[editar # 1]

Muy bien, un poco más de código:

De esta manera estoy revisando cada 10 segundos si el dispositivo está enchufado:

  private bool CheckUsbDeviceAvailability() { ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\WMI", "SELECT * FROM MSSerial_PortName WHERE PortName = '" + this.PortName + "'"); if (searcher.Get().Count > 0) return true; return false; } 

Ese es el evento de callback del puerto serie:

 void BarcodeScannerCallback(object sender, SerialDataReceivedEventArgs e) { Thread.Sleep(500); string data = this._barcodeScanner.ReadExisting().Replace(Convert.ToChar(2), Convert.ToChar(32)).Trim(); if (data.StartsWith("AX")) { string[] arrData = data.Split('\n'); this._barcodeScanner.StopAvailabilityThread(); Barcode code = new Barcode(arrData[0].Replace("\r", "")); if (CheckIfBarcodeExists(code)) this.UpdateBarcodeNode(code); else this.CreateBarcodeNode(code); BarcodeScannerCallbackEvent(sender, e, code); this._barcodeScanner.StartAvailabilityThread(); } this._barcodeScanner.ComDevicePluggedIn = ScannerDevice.ComAvailabilityState.Available; } 

si ya no responde, activará el “DeviceNotAvailableEvent ()”:

  void BarcodeScannerDeviceNotAvailableEvent() { this._barcodeScanner.Close(); this._barcodeScanner.Dispose(); } 

He anulado el evento Dispose de la clase “SerialPort” para que aborte el subproceso:

 protected override void Dispose(bool isDisposing) { if (isDisposing) { this._deviceAvailableThread.Abort(); } base.Dispose(isDisposing); } 

Los puertos serie datan de la edad de piedra de la informática. Ahí es donde enchufaste tu teletipo ASR-33 para comenzar a escribir en tu progtwig Fortran. La interfaz eléctrica es muy simple. También lo es la API de Windows para usar un puerto serie desde su propio código. Prácticamente cualquier entorno de tiempo de ejecución los respalda.

USB ha reemplazado completamente el hardware del puerto serie. Tiene una interfaz lógica mucho más avanzada para la máquina, compatible con muchos tipos diferentes de dispositivos. Y admite Plug and Play, lo que permite que el sistema operativo detecte cuándo se conecta o quita un dispositivo, así como la instalación automática del controlador del dispositivo, etcétera.

Sin embargo, esta flexibilidad tiene un precio, un dispositivo USB siempre necesita un controlador de dispositivo para ser utilizable. Los controladores de dispositivo no se crean iguales. Los diferentes controladores requieren diferentes formas de hablar con el dispositivo. Usualmente se hace a través de DeviceIoControl () o Read / WriteFile () pero esas son funciones de API muy opacas. En los primeros días del USB, los fabricantes de dispositivos proporcionaban una DLL que proporcionaba una API enriquecida para ocultar los detalles de la implementación.

Eso no funcionó tan bien, los fabricantes no son muy buenos para escribir buenas API y seguro que no les gusta apoyarlos . Entonces, una buena solución sería admitir una API estándar, una que esté disponible en cualquier máquina, compatible con cualquier tiempo de ejecución, documentada y mantenida por otra persona. Al igual que la API del puerto serie.

Eso no funcionó tan bien, los fabricantes no son muy buenos para escribir controladores de dispositivos que emulan puertos serie. El mayor problema con la API es que no tiene soporte para Plug and Play. Falta el núcleo de soporte, después de que todo el hardware del puerto serial no tiene la interfaz lógica para soportarlo. Existe algún soporte para detectar que un dispositivo está conectado a través de la línea de handshake de hardware de DTR, pero no admite soporte alguno para detectar que el puerto ya no está allí.

Separar el dispositivo USB es el problema. En un mundo ideal, el emulador incorporado en el controlador del dispositivo simplemente simularía que el puerto en serie aún está allí hasta que se cierre el último identificador del dispositivo. Esa sería la implementación lógica, dado que no hay forma de desencadenar un evento Plug and Play. Por alguna extraña razón que parece ser difícil de implementar. La mayoría de los controladores USB toman el atajo crummy, simplemente hacen que el dispositivo desaparezca incluso mientras está en uso .

Esto causa esgulps en cualquier código de modo de usuario que use el dispositivo. Que normalmente está escrito para suponer que es un puerto serie real y los puertos serie reales no desaparecen de repente. Al menos no sin dibujar una shiny chispa azul. Lo que sale mal es bastante impredecible porque depende de cómo responde el conductor a las solicitudes en un dispositivo que ya no está allí. Una excepción no detectable en un hilo de trabajo iniciado por SerialPort fue un error común. Parece que su controlador realmente se equivoca, genera un código de retorno de error en la solicitud del controlador MJ_CLOSE. Lo cual es algo lógico que se debe hacer por un controlador, después de todo, el dispositivo ya no está allí, pero es bastante insoluble desde su final. Usted tiene un mango y no puede cerrarlo. Eso es un arroyo sin remo.

Cada versión principal de .NET tenía un pequeño parche para las clases de SerialPort para intentar minimizar un poco la miseria. Pero hay una cantidad limitada que Microsoft puede hacer, detectar todos los errores y fingir que no sucedieron finalmente lleva a una clase que ya no brinda un buen diagnóstico, incluso con un buen controlador.

Así que los enfoques prácticos son:

  • utilice siempre el ícono de la bandeja Eliminar hardware de forma segura en Windows
  • usa la última versión de .NET
  • contacte al proveedor y solicite una actualización del controlador
  • vendedores de zanjas que suministran controladores pésimos
  • dígales a sus usuarios que, solo porque es lo único que puede hacer con un dispositivo USB, desconectarlo no resuelve ningún problema
  • haga que el cierre del puerto sea fácil y accesible en su UI
  • pegue el conector USB al puerto para que no se pueda quitar

La quinta viñeta también es lo que hace que los progtwigdores tengan problemas. Escribir código de puerto serie no es fácil, es muy asíncrono y el hilo de subprocesos que ejecuta el evento DataReceived es difícil de tratar. Cuando no puede diagnosticar el problema del software, tiende a culpar al hardware. Hay muy poco que puede hacer con el hardware, pero desenchúfelo. Mala idea. Ahora tienes dos problemas.

Este problema existe en .Net 2, 3, 3.5. Puedes usar el framework 4 (el problema no existe en .net 4)