2012-01-16 12 views
5

Mi sto tirando i capelli cercando di capire quando una porta seriale termina la chiusura in modo da poterla riaprire. Si scopre che CloseHandle() ritorna prima che la porta sia effettivamente sbloccata.CloseHandle() restituisce prima che la porta seriale sia effettivamente chiusa

sto aprendo una porta seriale utilizzando CreateFile(FILE_FLAG_OVERLAPPED), associandolo con un CompletionPort utilizzando CreateIoCompletionPort(), lettura/scrittura ad esso utilizzando ReadFile(), WriteFile() e chiudendo utilizzando CloseHandle().

Ho notato che se chiudo e riapuro una porta seriale abbastanza velocemente, viene restituito uno da CreateFile(). Ciò sta accadendo nonostante io stia aspettando il ritorno di CloseHandle(), quindi attendo che tutte le operazioni di lettura/scrittura in sospeso associate a quell'handle vengano restituite dalla porta di completamento. Sicuramente c'è un modo migliore :)

Come si chiude una porta seriale in modo sincrono? Si prega di non ripetere i loop, sleep() o qualche altro hack economico.

EDIT: Forse questo ha qualcosa a che fare con il mio utilizzo delle porte di completamento e FILE_FLAG_OVERLAPPED. Ricevo una richiamata quando le operazioni di lettura/scrittura sono complete. C'è una sorta di callback per la chiusura del porto?

+0

Forse correlato al driver della porta seriale specifico. Hai visto questo qui su SO: http://stackoverflow.com/questions/2948428/reopening-serial-port-fails-if-not-closed-properly-with-closehandle –

+0

Giusto per essere sicuro .... tu paradiso usare 'DuplicateHandle' ovunque? –

+0

Non sono sicuro che la porta di completamento sia correlata, ho sempre usato i callback APC con 'ReadFileEx' e' WriteFileEx'. Ciò è anche più semplice, poiché gli APC vengono eseguiti solo quando il thread entra in attesa, quindi non ci sono problemi di sincronizzazione tra thread (basta fare attenzione al re-entrancy). –

risposta

1

Credo che il problema sia con il driver che serve la porta COM. Quindi, non ci sarà alcuna API per "chiudere effettivamente" la porta COM.

BTW, dopo aver chiuso l'handle del file, non è necessario attendere il completamento di tutti gli I/O oustanding. Al momento CloseHandle restituisce tutti gli I/O in sospeso sono già completati/annullati, è sufficiente ricevere i callback in modo asincrono (tramite porta di completamento o coda APC, non importa).

In particolare i driver FTDI (quelli che emulano COM-> USB) sono noti per essere molto glitch.

Posso solo consigliare di provare a lavare i dati prima di chiudere la maniglia. È possibile attendere che tutti gli I/O completino prima dello chiudendo la porta COM (se applicabile per il proprio caso). In alternativa, è possibile chiamare SetCommMask e WaitCommEvent per assicurarsi che non ci siano dati in attesa. Speriamo che questo possa aiutare.

EDIT:

Vuol CloseHandle immediatamente (prima che ritorni) annullare tutti gli I/O in attesa sul file gestire?

In senso stretto - no.

Ci possono essere altri riferimenti all'oggetto file. Ad esempio, un codice in modalità utente può chiamare DuplicateHandle oppure un driver in modalità kernel può chiamare ObReferenceObjectByXXXX. In tal caso l'oggetto a cui si riferisce il manico non è necessariamente rilasciato.

Quando si chiude l'ultima maniglia, viene chiamato il numero di telefono DispatchCleanup del conducente. È necessario annullare tutti gli I/O in sospeso in base allo this.

Tuttavia, mentre un thread è all'interno di un CloseHandle - è possibile teoricamente rilasciare un altro I/O da un altro thread (se si è fortunati, l'handle sarà comunque valido). Durante la chiamata a una funzione I/O (ad esempio WriteFile o ecc.), Il sistema operativo aumenta temporaneamente il contatore di riferimento sull'oggetto. Ciò a sua volta può avviare un altro I/O, che non verrà annullato direttamente dall'invocazione CloseHandle.

Tuttavia, in questo caso, l'handle verrà chiuso da O/S immediatamente dopo l'emissione del nuovo I/O, poiché il conteggio dei riferimenti all'oggetto ha raggiunto nuovamente 0.

In modo che in uno scenario così pervertito possa verificarsi una situazione in cui immediatamente dopo una chiamata a CloseHandle non è possibile riaprire nuovamente il file.

+0

Sto usando la porta seriale che esce dalla scheda madre (usando i driver Microsoft incorporati). Non c'è adattatore COM in USB in gioco. Il problema con lo svuotamento dei dati è che potrebbe bloccarsi per sempre se il controllo del flusso hardware è abilitato. Idealmente mi piacerebbe interrompere tutti i comandi in sospeso, chiudere la porta e tornare dalla mia funzione una volta che la chiusura è stata fatta. Questo è tutto un punto controverso se non ci sono API per garantire una chiusura. PS: Come sai che 'CloseHandle()' annulla tutti gli I/O in sospeso prima di tornare? Non vedo una tale garanzia nelle specifiche. – Gili

+0

Vedi la mia modifica. Puoi anche provare a usare 'CancelIoEx'. E, naturalmente, non fare alcun I/O su un altro thread (ma spero che non lo faccia comunque) – valdo

+0

Ok, ho finito per usare un ciclo di tentativi quando provo ad aprire la porta. Grazie per le informazioni di riferimento. – Gili

0

Verificare che il lettore e il writer non funzionino correttamente (con handle non valido) dopo aver emesso la richiesta CloseHandle() (presumibilmente su un altro thread) prima di tentare di toccare nuovamente il dispositivo. È possibile utilizzarlo come cue per ripulire tutta la tua gestione io prima di tentare di emettere un altro CreateFile. Devo dire che i porti di completamento sembrano inutilmente barocchi.

+0

Bel trucco, ma inaffidabile. Perché la stessa maniglia può essere (teoricamente) riutilizzata. – valdo

+0

Inoltre potresti non avere alcuna lettura/scrittura in sospeso, quindi ora devi tenerne traccia. – Gili

0

Quando si apre una porta COM in un thread, il sistema operativo apre un altro thread nascosto per eseguire l'I/O. Sto cercando di risolvere anche la chiusura di una porta. Vicino come posso dire che un metodo che può funzionare è: 1) Sospendi il thread che ha aperto la porta COM, 2) PurgeComm() per arrestare tutto l'I/O e svuotare tutti i buffer di I/O di lettura/scrittura e 3) quindi provare per chiudere la porta COM. Potrebbe esserci un waitforsingleobject coinvolto per determinare quando il thread con la porta COM effettivamente sospende. A meno che la porta COM non debba assolutamente essere chiusa, sto pensando di lasciare la porta COM aperta e lasciare che WinProc elabori il comando IDM_Exit per chiudere la porta COM, i thread e l'applicazione. Non quello che volevo, ma un'opzione con cui avrei potuto vivere. Sto usando una connessione USB a porta seriale e non sono sicuro di quali problemi mi causino. So che se si utilizza un'interfaccia da USB a porta seriale è necessario impostare il pin 20 alto (DTR). Il Pin 20 aiuta ad alimentare l'interfaccia, ma fornisce solo circa 30ma circa. Se tutto questo funzionerà farò un aggiornamento a questo post e ti farò sapere come sono riuscito a chiudere la porta COM. Windows ha fatto un casino del driver Serial Port e sembra essere contento di lasciare le cose in disordine!