2013-11-09 11 views
6

Sto aprendo un file con per la lettura che avevo creato in precedenza nella cartella% TEMP% dell'utente, utilizzando il seguente codice:L'utilizzo di FileShare.Delete può causare una eccezione non autorizzata?

new FileStream(cacheFileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete); 

Sui computer di alcuni utenti, questo a volte genera un UnauthorizedAccessException con il messaggio " L'accesso al percorso ... è negato ". Non sono stato in grado di riprodurre questo. La mia ipotesi iniziale è che un motore antivirus o di indicizzazione stia facendo qualcosa di funky, ma ho anche notato che questo codice utilizza "FileShare.Delete", che non sono sicuro dovrebbe essere lì.

C'è uno scenario in cui l'utilizzo di "FileShare.Delete" porta a UnauthorizedAccessException?

risposta

12

Sì, FileShare.Delete tende a causare questo problema. Utilizzato da qualsiasi programma in esecuzione in background e scansiona i file, gli indicizzatori di file e gli antivirus sono gli esempi più comuni.

FileShare.Delete consente di un altro processo per eliminare il file, anche se il processo in background ha ancora il file aperto e sta leggendo da esso. Questo altro processo sarà ignaro che il file non è effettivamente scomparso, perché tutto sa che il file è stato effettivamente cancellato.

Il problema inizia quando quell'altro processo si basa sul file che viene effettivamente rimosso e fa qualcos'altro. Comunemente attivato creando un nuovo file con lo stesso nome. In particolare un modo molto poco saggio per salvare un file poiché causerà la completa perdita di dati senza un backup quando il salvataggio fallisce, ma questo errore è molto comune.

Ciò non riuscirà perché la voce di directory per il file è ancora presente, non scomparirà fino a quando l'ultimo processo che ha il file aperto non chiude l'handle. Qualsiasi altro processo che tenta di aprire nuovamente il file verrà schiaffeggiato con errore 5, "accesso negato". Compreso quel processo che ha cancellato il file e tenta di ricrearlo.

La soluzione è di utilizzare sempre i salvataggi "transazionali", rinominando il file prima di provare a sovrascriverlo. Disponibile in .NET con File.Replace(), nel file nativo winapi con ReplaceFile(). Inoltre facilmente fatto a mano, il flusso di lavoro è:

  1. eliminare il file di backup, arrestare se non è riuscito
  2. rinominare il vecchio file al nome del file di backup, stop se non è riuscita
  3. scrivere il nuovo file utilizzando il nome del file originale , rinominare il backup del lotto se non è riuscito
  4. eliminare il file di backup, ignorare il fallimento

Fase 2 assicura che non ci sarà mai alcuna perdita di dati, il file originale rimane intatto se qualcosa va storto. Il passaggio 4 garantisce FileShare.La cancellazione funzionerà come previsto, il file di backup sparirà alla fine quando altri processi chiuderanno i loro punti di manipolazione.

3

Ho trovato uno scenario che riproduce questo:

static void Main(string[] args) 
    { 
     string cacheFileName = @"C:\temp.txt"; 
     using (var filestream = new FileStream(cacheFileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete, 4096, FileOptions.SequentialScan)) 
     { 
      filestream.Read(new byte[100], 1, 1); 
      Console.ReadLine(); 
      GC.KeepAlive(filestream); 
     } 
     Console.WriteLine("Done!"); 
    } 
} 

creare una "C: \ temp.txt" del file, quindi eseguire questo programma. Prova ad eliminare il file con Explorer/TotalCommander, non si lamenterà ma non cancellerà neanche il file. Quindi, eseguire nuovamente il programma e genererà l'eccezione UnauthorizedAccessException. Dopo aver chiuso entrambi .exes, sembra che il file sia stato finalmente cancellato.

La rimozione di "FileShare.Delete" risolve questo problema, poiché semplicemente non consente di provare a eliminare il file mentre è in uso.