Quando apro un file, voglio sapere se è utilizzato da un altro processo, in modo che posso eseguire un trattamento speciale; qualsiasi altra IOException che farò esplodere. La proprietà Message di IOException contiene "Il processo non può accedere al file 'pippo' perché è utilizzato da un altro processo.", Ma questo non è adatto per il rilevamento programmatico. Qual è il modo più sicuro e più efficace per rilevare un file utilizzato da un altro processo?Come dire se un IOException catturato è causato dal file in uso da un altro processo, senza ricorrere a parsing proprietà Message della un'eccezione
risposta
Questa particolare versione di IOException
viene generata quando il codice di errore restituito dalla funzione nativa Win32 è ERROR_SHARING_VIOLATION
(Documentation). Ha il valore numerico di 0x20
, ma in realtà è memorizzato come 0x80070020
sulla HRESULT
proprietà di eccezione (è il risultato di chiamare MakeHRFromErrorCode).
Quindi il modo programmatico di controllo per una violazione di condivisione sta controllando la proprietà HResult
sul IOException
per il valore 0x80070020
.
public static bool IsSharingViolation(this IOException ex) {
return 0x80070020 == Marshal.GetHRForException(ex);
}
Tuttavia mi chiedo che cosa esattamente si vuole fare nello scenario che è stato gettato come il risultato di una violazione di condivisione. Nel momento in cui viene lanciata l'eccezione, l'altro processo potrebbe uscire e quindi rimuovere la violazione.
Perché non utilizzare 'ex.HResult' direttamente invece di' Marshal.GetHRForException'? –
@Anders perché la proprietà HResult ha un livello di accessibilità protetto quindi non è generalmente accessibile. – JaredPar
Risposta fantastica JaredPar, grazie ... In risposta alla tua domanda; Ho bisogno di rilevare questo scenario perché ho bisogno di un messaggio amichevole per il livello di presentazione - è ok se il conflitto potrebbe non esistere il momento dopo, a causa della natura della mia app ... grazie ancora –
non ho abbastanza "rep" per commentare quindi spero che questo "risposta" va bene ...
La risposta accettata è esattamente quello che cercavo e funziona perfettamente, ma la gente qui e su simili le domande hanno messo in dubbio l'utilità di controllare se un file è bloccato. È corretto che una funzione di utilità per verificare se un file è bloccato non è molto utile perché nella dichiarazione successiva lo stato potrebbe essere cambiato.
Ma il modello di tentare un'operazione di blocco e di rispondere in modo diverso per bloccare errori contro errori generali è valido e utile. La cosa più ovvia da fare è aspettare un po 'e riprovare l'operazione. Questo può essere generalizzato in una funzione di supporto come questo:
protected static void RetryLock(Action work) {
// Retry LOCK_MAX_RETRIES times if file is locked by another process
for (int i = 1; i <= LOCK_MAX_RETRIES; i++) {
try {
work();
return;
} catch (IOException ex) {
if (i == LOCK_MAX_RETRIES || (uint) ex.HResult != 0x80070020) {
throw;
} else {
// Min should be long enough to generally allow other process to finish
// while max should be short enough such that RETRIES * MAX isn't intolerable
Misc.SleepRandom(LOCK_MIN_SLEEP_MS, LOCK_MAX_SLEEP_MS);
}
}
}
} // RetryLock
... che può quindi essere utilizzato in questo modo:
public string DoSomething() {
string strReturn = null;
string strPath = @"C:\Some\File\Path.txt";
// Do some initial work...
//----------------------------------------------------------------------------------------------------
// NESTED FUNCTION to do main logic, RetryLock will retry up to N times on lock failures
Action doWork = delegate {
using (FileStream objFile = File.Open(strPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) {
// Does work here if lock succeeded, else File.Open will throw ex
strReturn = new StreamReader(objFile).ReadLine();
}
}; // delegate doWork
//----------------------------------------------------------------------------------------------------
RetryLock(doWork); // Throws original ex if non-locking related or tried max times
return strReturn;
}
... comunque, solo la pubblicazione nel caso in cui una persona con esigenze simili reperti il modello utile.
Se si utilizza C# 6 questa è la situazione ideale per utilizzare le eccezioni filtrati 'catch (IOException ex) quando (i
è il tipo davvero un'IOException o è uno dei tipi di derivati da IOException? –
Per motivi di interesse, perché è necessario rilevarlo in modo programmatico? Cosa pensi di fare una volta scoperto che un altro processo sta usando il tuo file? –
@ Marco Byers, vedi il mio commento contro la risposta accettata. –