2013-05-14 12 views
14

Ho un problema di condivisione file in cui il mio processo sta tentando di leggere un file di registro mentre è ancora aperto da NLog. Nel diagnosticare il problema, ho trovato qualcosa di sorprendente. Quanto segue non riesce:La condivisione di file non funziona come previsto

using (var fileStream1 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read)) 
using (var fileStream2 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.Read)) 
{ 
} 

Il secondo FileStream chiamata al costruttore fallisce con:

System.IO.IOException was unhandled 
    Message=The process cannot access the file 'c:\...\test.file' because it is being used by another process. 
    Source=mscorlib 
    StackTrace: 
     at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 
     at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath) 
     at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share) 

Questo nonostante il fatto che il primo FileStream indica la sua volontà di condividere la lettura. Quello che ho trovato ancora più sorprendente è che questo funziona:

using (var fileStream1 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read)) 
using (var fileStream2 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
{ 
} 

Uhm, sì, chiedendo accesso più quando si apre il secondo flusso in realtà bypassa il problema. Sono completamente sconcertato sul perché questo sia il caso, e posso solo supporre che io stia fraintendendo qualcosa. Ho letto i documenti dell'API, ma supportano il mio attuale modello mentale per come dovrebbe funzionare, contrariamente a come funziona.

Ecco alcune citazioni sostegno del docs:

Un utilizzo tipico di questa enumerazione è definire se due processi possono leggere simultaneamente dallo stesso file. Ad esempio, se un file è aperto e Read è specificato, altri utenti possono aprire il file per la lettura di ma non per la scrittura.

Ecco un altro gioiello:

Il seguente costruttore FileStream apre un file esistente e sovvenzioni accesso in sola lettura ad altri utenti (lettura).

FileStream s2 = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.Read);

Qualcuno può far luce su questo comportamento. Sto testando questo su .NET 4% Windows XP.

risposta

15
var fileStream2 = new FileStream(..., FileShare.Read) 

Questo fa scattare molti programmatori. Tutti pensano che questo aggiunto abbia letto la condivisione. Non è stato così, la richiesta di accesso al file originale ha già permesso di leggerla e specificarla di nuovo non cambia nulla. Invece, nega la condivisione di scrittura. E questo non può funzionare perché qualcuno ha già accesso in scrittura. E lo sta usando, non puoi rimuovere quel diritto. Quindi la tua richiesta di accesso al file fallirà.

Il numero deve essere includere FileShare.Write.

+1

Grazie. Quindi questo equivoco è così diffuso che persino i documenti sono sbagliati/fuorvianti? –

+0

Vuoi dire includere 'FileShare.Write' nel secondo stream, giusto? – rookie1024

0

quarto parametro si passa

quota
Una costante che determina come il file verrà condiviso dai processi.

determina in quale modalità gli altri possono aprire il file. Quindi, ovviamente, quando stai cercando di aprire il file con la modalità di condivisione file "leggi" e hai già aperto lo stesso file in modalità Scrittura, l'operazione fallisce.

+0

Non è terzo, è quarto. E 'FileShare.Read' _Consente l'apertura successiva del file per la lettura._ –

+0

scusate, corretto :) – elevener

+0

FileShare.Read -" Consente l'apertura successiva del file per la lettura "- ma non per la scrittura. Hai già un file aperto per scrivere ciò che è proibito con quella modalità di condivisione. Cosa dovrebbe fare la funzione? – elevener

1

In realtà, è possibile che fileStream2 non possa modificare l'accesso successivo a un file che è già aperto per la scrittura (o l'aggiunta) da fileStream1.

fileStream2 aprirà correttamente il file lasciando uno FileShare.Read come "legacy" per l'accesso successivo solo se non ci sono processi che hanno già accesso al file Write. Ancor più, nel nostro esempio stiamo parlando dello stesso processo. Non avrebbe molto senso modificare le proprietà di un flusso di file da un altro flusso di file, vero?

Forse il seguente confronto spiega ancora meglio:

// works 
using (var fileStream1 = new FileStream("test.file", FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite)) 
using (var fileStream2 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read)) 
using (var fileStream3 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
{ 
} 

// fails 
using (var fileStream1 = new FileStream("test.file", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite)) 
using (var fileStream2 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read)) 
using (var fileStream3 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
{ 
} 

A mio parere, la descrizione frase per FileShare.Read:

Consente successiva apertura del file per la lettura.

dovrebbe essere letto come

Il successivo accesso al file è limitato solo per la lettura, compreso l'accesso delle serrature già esistenti.

[Update]

Non ho analizzato attraverso il codice, ma sembra che questi due link potrebbe far luce sul funzionamento interno del costruttore:

The internal FileStream ctor

The internal FileStream Init method

+0

Ancora non ha fatto clic, nonostante abbia letto più volte la tua risposta. Trovo difficile credere che i documenti API siano così completamente e completamente sbagliati. per esempio. "Ad esempio, se un file è aperto e' Read' è specificato, altri utenti possono aprire il file per la lettura ma non per la scrittura. " Questo è esattamente ciò che ho cercato di fare nel mio esempio, ma non funziona. –

+0

@KentBoogaart: Non so se è rilevante (non lo capisco) ma una frase chiave potrebbe essere "altri utenti". Un test migliore di "altri utenti" sarebbe in un test multi-thread. – Chris

+0

@Chris: il mio scenario reale è multi-thread (tentando di raccogliere i log sul thread diagnostico in background). Inoltre, i documenti dichiarano esplicitamente "da questo processo o da un altro processo" quando descrivono il comportamento di condivisione. –

1

Penso di aver trovato la risposta nella documentazione per CreateFile.

Nella discussione del parametro dwShareMode, si dice:

FILE_SHARE_READ 0x00000001 Abilita successive operazioni aperte su un file o un dispositivo per richiedere l'accesso in lettura. In caso contrario, altri processi non possono aprire il file o il dispositivo se richiedono l'accesso in lettura. Se questo flag non è specificato, ma il file o il dispositivo è stato aperto per l'accesso in lettura, la funzione ha esito negativo.

FILE_SHARE_WRITE 0x00000002 Consente successive operazioni aperte su un file o dispositivo per richiedere l'accesso in scrittura. In caso contrario, altri processi non possono aprire il file o il dispositivo se richiedono l'accesso in scrittura. Se questo flag non è specificato, ma il file o il dispositivo è stato aperto per l'accesso in scrittura o ha un mapping di file con accesso in scrittura, la funzione non riesce.

Ciò sostanzialmente cambia la mia comprensione di come funziona la condivisione di file.

Problemi correlati