2012-01-16 4 views
5

Estoy tirando de mi cabello tratando de averiguar cuándo un puerto serie termina de cerrarse para que pueda volver a abrirlo. Resulta que CloseHandle() regresa antes de que el puerto esté realmente desbloqueado.CloseHandle() regresa antes de que el puerto serie esté realmente cerrado

Estoy abriendo un puerto serie utilizando CreateFile(FILE_FLAG_OVERLAPPED), asociándolo con un CompletionPort usando CreateIoCompletionPort(), leer/escribir en él usando ReadFile(), WriteFile() y cerrándola usando CloseHandle().

Me he dado cuenta de que si cierro y vuelvo a abrir un puerto serie con la suficiente rapidez, obtengo un ERROR_ACCESS_DENIED de vuelta desde CreateFile(). Esto está sucediendo a pesar del hecho de que estoy esperando que regrese el CloseHandle(), luego esperando que todas las operaciones pendientes de lectura/escritura asociadas con ese identificador vuelvan del puerto de finalización. Seguramente hay una mejor manera :)

¿Cómo cierro un puerto serie sincrónicamente? Por favor, no vuelva a intentar loops, sleep() o algunos otros hacks baratos.

EDIT: Quizás esto tiene algo que ver con mi uso de los puertos de terminación y FILE_FLAG_OVERLAPPED. Recibo una devolución de llamada cuando se completan las operaciones de lectura/escritura. ¿Hay algún tipo de devolución de llamada para el cierre del puerto?

+0

Quizás relacionado con su controlador de puerto serie específico. ¿Has visto esto aquí en SO: http://stackoverflow.com/questions/2948428/reopening-serial-port-fails-if-not-closed-properly-with-closehandle –

+0

Sólo para estar seguro ... que has guardado 't usado 'DuplicateHandle' en cualquier lugar? –

+0

No estoy seguro de si el puerto de terminación está relacionado, siempre he utilizado devoluciones de llamada de APC con 'ReadFileEx' y' WriteFileEx'. Eso también es más simple, ya que las APC solo se ejecutan, entonces el hilo entra en espera alertable, por lo que no hay problemas de sincronización cruzada (solo tenga cuidado con la reentrada). –

Respuesta

1

Creo que el problema es con el controlador que sirve el puerto COM. Por lo tanto, no habrá API para "cerrar realmente" el puerto COM.

BTW, después de cerrar el identificador del archivo, no hay necesidad de esperar a que todas las E/S sobresalientes se completen con errores. En el momento en que CloseHandle devuelve todas las E/S pendientes son ya completadas/canceladas, usted acaba de recibir las devoluciones de llamada de forma asincrónica (ya sea a través del puerto de terminación o la cola APC, no importa).

Específicamente, los controladores FTDI (aquellos que emulan COM-> USB) son muy difíciles.

Es posible que solo recomiende intentar vaciar los datos antes de cerrar la manija. Puede esperar a que todas las E/S completen antes de cerrando el puerto COM (si esto es aplicable para su caso). Alternativamente, puede llamar al SetCommMask y WaitCommEvent para asegurarse de que no haya datos de envío pendientes. Espero que esto pueda ayudar.

EDIT:

¿Se CloseHandle inmediatamente (antes de que vuelva) cancelar toda la espera de E/S en el archivo de manejar?

Estrictamente hablando - no.

Puede haber otras referencias para el objeto de archivo. Por ejemplo, un código de modo de usuario puede llamar al DuplicateHandle, o un controlador en modo kernel puede llamar al ObReferenceObjectByXXXX. En tal caso, el objeto al que se refiere el identificador no se libera necesariamente.

Cuando se cierra el último identificador se llama al controlador DispatchCleanup. Debe cancelar todas las E/S pendientes según this.

Sin embargo, mientras que un hilo está dentro de un CloseHandle - en teoría, puede emitir otro I/O de otro hilo (si tiene suerte, el identificador seguirá siendo válido). Mientras está en la llamada a una función de E/S (como WriteFile o etc.), el sistema operativo aumenta temporalmente el contador de referencia al objeto. Esto a su vez puede iniciar otra E/S, que no se cancelará directamente mediante la invocación CloseHandle.

Sin embargo, en este caso el O/S cerrará el asa inmediatamente después de que se emite la nueva E/S, porque el recuento de referencias al objeto alcanzó 0 nuevamente.

Para que en un escenario tan pervertido pueda ocurrir una situación en la que inmediatamente después de una llamada al CloseHandle no pueda volver a abrir el archivo.

+0

Estoy usando el puerto serie que sale de la placa base (usando los controladores integrados de Microsoft). No hay un adaptador COM a USB en juego. El problema con el lavado de datos es que podría bloquear para siempre si el control de flujo de hardware está habilitado. Idealmente, me gustaría abortar todos los comandos pendientes, cerrar el puerto y regresar de mi función una vez que el cierre haya finalizado. Eso es todo un punto discutible si no hay API para asegurar un cierre. PD: ¿Cómo sabes que 'CloseHandle()' cancela todas las E/S pendientes antes de volver? No veo tal garantía en la especificación. – Gili

+0

Por favor, mira mi edición. También puede intentar usar 'CancelIoEx'. Y, por supuesto, no haga ninguna E/S en otro hilo mientras tanto (espero que no haga esto de todos modos) – valdo

+0

De acuerdo, terminé usando un ciclo de reintento cuando intento abrir el puerto. Gracias por la información de referencia. – Gili

0

Asegúrese de que su lector y su escritor fallan (con un identificador no válido) después de haber emitido la solicitud CloseHandle() en otro hilo, antes de intentar tocar el dispositivo nuevamente. Puede usar esto como una señal para limpiar todo su manejo antes de intentar emitir otro CreateFile. Debo decir que los puertos de terminación parecen innecesariamente barrocos.

+0

Buen truco, pero poco confiable. Debido a que el mismo mango puede ser (teóricamente) reutilizado. – valdo

+0

Además, es posible que no tenga lecturas o escrituras sobresalientes, por lo que ahora también debe hacer un seguimiento de eso. – Gili

0

Cuando abre un puerto COM en una secuencia, el sistema operativo abre otra cadena oculta para hacer la E/S. Estoy tratando de resolver el cierre de un puerto también. Cerca de lo que puedo decir, un método que puede funcionar es: 1) Suspender el hilo que abrió el puerto COM, 2) PurgarComm() para detener todas las E/S y vaciar todos los buffers de E/S de lectura/escritura, y 3) luego intentar para cerrar el puerto COM. Puede haber una espera para un solo objeto involucrado para determinar cuándo se suspende el hilo con el puerto COM. A menos que el puerto COM sea absolutamente necesario cerrar, estoy considerando dejar abierto el puerto COM y dejar que el WinProc procese el comando IDM_Exit para cerrar el puerto COM, los hilos y la aplicación. No es lo que quería, sino una opción con la que podría vivir. Estoy usando una conexión USB a puerto serie y no estoy seguro de qué problemas me está causando. Sé que si está utilizando una interfaz USB a puerto serie, necesita configurar el pin 20 alto (DTR). El pin 20 ayuda a alimentar la interfaz, pero solo proporciona alrededor de 30 ma más o menos. Si todo esto funciona, haré una actualización de esta publicación y le diré cómo puedo cerrar el puerto COM. ¡Windows ha hecho un lío con el controlador del puerto serial y parece estar contento de dejar las cosas en desorden!

Cuestiones relacionadas