2010-03-19 8 views
7

La maggior parte del mio lavoro quotidiano di programmazione in Windows si basa oggi su operazioni di I/O di qualsiasi tipo (pipe, console, file, socket, ...). Sono ben consapevole dei diversi metodi di lettura e scrittura da/a diversi tipi di handle (sincrono, asincrono in attesa di completamento sugli eventi, attesa di file HANDLE, porte di I/O di completamento e I/O alertable). Usiamo molti di quelli.E 'possibile modificare HANDLE che è stato aperto per l'I/O sincrono da aprire per l'I/O asincrono durante la sua durata?

Per alcune delle nostre applicazioni sarebbe molto utile avere un solo modo per trattare tutte le maniglie. Voglio dire, il programma potrebbe non sapere, che tipo di handle ha ricevuto e vorremmo usare, diciamo, porte di completamento I/O per tutti.

Quindi, prima vorrei chiedere:

Supponiamo che io ho una maniglia:

HANDLE h; 

che è stato ricevuto da mio processo per l'I/O da qualche parte. C'è un modo facile e affidabile per scoprire con quali flag è stato creato? La bandiera principale in questione è FILE_FLAG_OVERLAPPED.

L'unico modo a me noto finora è provare a registrare tale handle nella porta di completamento I/O (utilizzando CreateIoCompletionPort()). Se questo succede, l'handle è stato creato con FILE_FLAG_OVERLAPPED. Tuttavia, è necessario utilizzare solo la porta di completamento I/O, poiché non è possibile annullare la registrazione dell'handle senza chiudere lo stesso HANDLEh.

fornendo c'è un modo semplice per determinare la presenza di FILE_FLAG_OVERLAPPED, sarebbe venuto mia seconda domanda:

C'è un modo come aggiungere come bandiera per maniglia già esistente? Ciò renderebbe un handle che è stato originariamente aperto per le operazioni sincrone per essere aperto per asincrono. Ci sarebbe un modo come creare opposto (rimuovere il FILE_FLAG_OVERLAPPED per creare l'handle sincrono da asincrono)?

Non ho trovato alcun modo diretto dopo aver letto tramite MSDN e googling molto. Ci sarebbe almeno qualche trucco che potrebbe fare lo stesso? Come ricreare l'handle nello stesso modo usando la funzione CreateFile() o qualcosa di simile? Qualcosa anche parzialmente documentato o non documentato?

Il luogo principale in cui avrei bisogno di questo, è determinare il modo in cui (o modificare il modo) il processo dovrebbe leggere/scrivere dagli handle inviati ad esso da applicazioni di terze parti. Non possiamo controllare in che modo i prodotti di terze parti creano le loro maniglie.

Cari guru di Windows: aiuto per favore!

Per quanto riguarda

Martin

+0

Si noti che il trucco 'CreateIoCompletionPort()' è in realtà abbastanza accurato ipotizzando che si desideri effettivamente utilizzare l'handle con quella porta specifica. Non ci avevo pensato! –

risposta

2

3 anni passarono e Windows 8 è stato rilasciato. Grazie alla regressione introdotta nell'implementazione della console in Windows 8, ho dovuto fare qualcosa riguardo al problema che ha fatto scattare questa domanda. Quindi ho finalmente provato a utilizzare la chiamata di funzione ReOpenFile().

In una frase: per i miei scopi è inutile.

L'API ReOpenFile() viene utilizzata per "prendere un handle di file esistente e ottenere un altro handle con un diverso set di diritti di accesso". Almeno questo è indicato nel original article.

ho cercato di usare il ReOpenFile() sulla maniglia di ingresso Console:

stdin_in = GetStdHandle(STD_INPUT_HANDLE); 
    stdin_in_operlapped = ReOpenFile(stdin_in, GENERIC_READ | GENERIC_WRITE, 
            FILE_SHARE_READ, FILE_FLAG_OVERLAPPED); 
    if (stdin_in_operlapped == INVALID_HANDLE_VALUE) 
    { 
     my_debug("failed to ReOpen stdin handle with OVERLAPPED flag: %d", GetLastError()); 
     exit(1); 
    } 

E che cosa ottengo è: Errore 1168: “Elemento non trovato”. "Grazie Microsoft". Non cercherò nemmeno di usarlo per pipe anonime, poiché la documentazione riporta:

"Le operazioni di lettura e scrittura asincrone (sovrapposte) non sono supportate da pipe anonime. Ciò significa che non è possibile utilizzare le funzioni ReadFileEx e WriteFileEx con pipe anonime. Inoltre, il parametro lpOverlapped di ReadFile e WriteFile viene ignorato quando queste funzioni vengono utilizzate con pipe anonime. "

Grazie, gente, tutto per i vostri suggerimenti. Durante la lettura da handle in modo asincrono, si deve essere pronti anche per il fatto che l'operazione potrebbe essere completata in modo sincrono. Questo lo so. Il motivo principale per cui ho fatto questa domanda è:

quando una lettura sincrona è stata emessa su alcuni oggetti (almeno pipe anonime e input di console in Windows 8) quindi chiamare CloseHandle() da un altro thread sullo stesso handle o falliscono, o si bloccano, fino a quando il ReadFile() non viene completato; ciò significa che si bloccherà indefinitamente in molti casi. Questo è il motivo per cui ho voluto sostituire l'handle sincrono con asincrono.

Ora mi è chiaro che semplicemente nei sistemi operativi Windows non è possibile cancellare alcune operazioni di lettura in modo diretto. Durante la lettura da handle sincroni, è sufficiente uscire dall'applicazione anche se ReadFile() sta ancora leggendo da un handle in alcuni thread, perché è semplicemente impossibile attivare in modo affidabile un'operazione di lettura. Sapere ... Nel SO più recente è possibile annullare quell'operazione. Tuttavia, non c'è modo di sapere che il thread è già nella chiamata a ReadFile() o non ancora. Se ReadFile() non viene ancora chiamato, non vi è alcuna operazione da annullare e la lettura successiva verrà interrotta. L'unico modo sarebbe chiudere l'handle, ma quell'operazione si blocca o non riesce su alcuni oggetti e su alcuni sistemi operativi. L'unica soluzione corretta è l'I/O asincrono.Ma, come ho detto all'inizio, la nostra APP è avviata da app di terze parti e non possiamo costringerli tutti a creare sempre pipe denominate con flag sovrapposti per lo stdio.

Sono rinunciare e andare a implementare brutte hack brutto ... Dovremo continuare a leggere senza struttura OVERLAPPED dalle maniglie che sono stati creati con la bandiera OVERLAPPED, e perde le maniglie e le discussioni ....

+0

Il mio consiglio sarebbe: non usare mai tubi anonimi! La funzione API Win32 ha un comportamento sostanzialmente identico alla creazione di un listener di pipe con un nome casuale ('CreateNamedPipe') seguito da' ConnectNamedPipe' e 'CreateFile' (per sicurezza bisognerebbe quindi passare un nonce sul pipe per confermare un altro processo non connettersi). L'utilizzo del proprio wrapper 'CreatePipe2' ti dà il controllo di cui hai bisogno se le pipe sono sovrapposte o meno. E, se sto leggendo correttamente i documenti, la pipe risultante gestisce _can_ può essere riaperta con 'ReOpenFile' per cambiare la modalità sovrapposta. –

+0

Errore perché si utilizza un handle CONSOLE, non un handle di file. – Demi

+0

@NicholasWilson Questo perché le pipe anonime * sono * implementate come pipe nominate sotto il cofano! – Demi

1

test bandiere manico dovrebbe essere fatto probabilmente allo stesso modo di testare i permessi del manico è stato creato con. Provalo. Se l'API non funziona, prova il fallback. Se fallisce, restituisci un errore.

Penso che la cosa veramente significativa sia il modo in cui la documentazione di ReadFile dice "Se hFile è aperto con FILE_FLAG_OVERLAPPED, la funzione ... può segnalare erroneamente che l'operazione di lettura è completa."

La mia interpretazione dell'errore è, (e la domanda che devi porre a te stesso è): se fosse possibile controllare lo stato sovrapposto di un handle di file, perché ReadFile non dovrebbe fare quel controllo e quindi convalidare il OVERLAPPED struttura di conseguenza, per fallire esplicitamente se chiamato in modo non sovrapposto con un handle sovrapposto?

+0

Sto chiedendo quella domanda e molti simili. sfortunatamente, non ci sono risposte dirette fornite da Microsoft. Questo è il motivo per cui ti sto chiedendo qui. Non ho trovato nessun altro modo per testare la presenza di flag OVERLAPPED quindi sopra descritto. –

4

Vedo che ero un cattivo lettore di MSDN:/Ho totalmente perso la funzione ReOpenFile() che è stata introdotta probabilmente già nel giugno 2003 in Windows Server 2003 (secondo this article). Per difendermi almeno un po ': mi aspetto che la descrizione CreateFile() faccia riferimento alla descrizione ReOpenFile(). C'è un riferimento su ReOpenFile() pagina alla pagina CreateFile(), ma non viceversa.

Questa funzione sembra abilitare esattamente ciò di cui ho bisogno: aggiunta o rimozione di FILE_FLAG_OVELRAPPED a/da maniglie già esistenti creando una nuova maniglia con le proprietà desiderate! MrGreen Non l'ho ancora provato, però. È, sfortunatamente, disponibile solo su Windows 2003 Server, Windows Vista e successivi. La domanda relativa alle precedenti versioni del sistema operativo ha avuto risposta here. La funzione non esiste nell'API pubblica nel sistema operativo precedente a Windows 2003 Server. Viene utilizzato dall'implementazione sottostante, ma non è disponibile per gli sviluppatori su tali sistemi (Non supportato).

Ciò significa praticamente che non c'è speranza per me almeno per i prossimi anni, fino a quando non abbandoneremo il supporto per le piattaforme Windows precedenti. Significa anche che la situazione relativa all'I/O è stata DAVVERO male su OS più vecchio di Windows Vista. Un'altra parte dolorosa che mancava del tutto era la possibilità di cancellare I/O sincroni e asincroni su quei vecchi sistemi.

Inoltre, mi manca ancora una parte della risposta: la presenza di bandiere può essere verificata con qualsiasi mezzo? Non ho trovato la funzione per farlo. Ciò significa che se vogliamo garantire la presenza di alcuni flag nell'oggetto file, il file deve sempre essere riaperto.

+0

Infatti, se cerco attraverso la mia copia locale di MSDN, vedo che la funzione non è menzionata (referenziata) assolutamente da nessuna parte! È elencato solo nell'elenco delle funzioni di gestione dei file. Anche Google offre sorprendentemente pochi successi. Solleva alcuni dubbi nella sua affidabilità. Sono desideroso di eseguire alcuni primi test. Mi piacerebbe vedere l'handle di Console con flag OVERLAPPED incluso per esempio. –

+0

"è possibile verificare la presenza di bandiere con qualsiasi mezzo?" buona domanda, qualche risposta? – elmarco

+1

Nota per i lettori: 'ReOpenFile()' funziona in modo ordinato per i file in quanto è possibile riaprire un flusso di file e aggiungere il supporto I/O asincrono al nuovo handle, ma non funziona su pipe anonime (restituisce sempre 'ERROR_PIPE_BUSY'). –

2

Se ho capito cosa stai cercando, vorrei suggerire che non ti interessa se è stato aperto con la bandiera sovrapposta o meno. Credo che si possa tranquillamente passare in una struttura OVERLAPPED in entrambi i casi sincroni e asincroni. Il tuo codice deve essere in grado di gestire ReadFile() restituendo false e GetLastError() restituendo ERROR_IO_PENDING. Avrete anche bisogno di aggiungere le chiamate appropriate per GetOverlappedResult(), WaitForSingleObject(), ecc

l'articolo di MSDN sulla ReadFile() ha alcune utili informazioni su questo sotto "Considerazioni per lavorare con i file handle sincrone", e "Considerazioni per lavorare con file di asincrona gestisce "nella sezione" Sincronizzazione e posizione file ".

+0

"Credo", hmm .. puoi confermare? :) – elmarco

1

non so un modo per determinare la bandiera di una maniglia e gli effetti collaterali dell'utilizzo di ReOpen api, ma dal momento che il vostro obiettivo ero

sarebbe molto utile avere solo un modo per trattare tutti i manici

Se si desidera un comportamento sincrono (intendo utilizzando API sincrona per non sovrapposti maniglie e asincrone api alimentate con struttura OVERLAPPED con conseguente attesa per l'evento del sovrapposte) si può sempre utilizzare l'API asincrona anche se la maniglia è stato aperto in modalità non sovrapposta come già affermato da @Brett
posso confermare che questo funziona (almeno per named pipe) es:

void connectSynchronous(HANDLE hPipeThatWeDontKnowItsFlag){ 
    ... 
    BOOL bRet = ::ConnectNamedPipe(hPipeThatWeDontKnowItsFlag, pOverlapped); 

    if(bRet == FALSE){ 
     DWORD dwLastErr = ::GetLastError(); 

     if(dwLastErr == ERROR_IO_PENDING){ 
      //The handle was opened for asynchronous IO so we have to wait for the operation 
      ...waitFor on the overlapped hEvent; 

     }else if(dwLastErr == ERROR_PIPE_CONNECTED){ 
      //The handle was opened for synchronous IO and the client was already connected before this call: that's OK! 
      return; 
     }else{ 
      throw Error(dwLastErr); 
     } 
    }/*else{ 
     //The handle was opened for synchronous IO and the client has connected: all OK 
    }*/ 
} 
0

Un'alternativa all'hack di CreateIoCompletionPort consiste nel fare un ReadFile a zero byte con un lpOverlapped NULL. Se fallisce con ERROR_INVALID_PARAMETER presume che sia stato aperto con FILE_FLAG_OVERLAPPED.

Problemi correlati