2010-04-02 25 views
10

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

+0

è il tipo davvero un'IOException o è uno dei tipi di derivati ​​da IOException? –

+1

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? –

+0

@ Marco Byers, vedi il mio commento contro la risposta accettata. –

risposta

13

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.

+1

Perché non utilizzare 'ex.HResult' direttamente invece di' Marshal.GetHRForException'? –

+2

@Anders perché la proprietà HResult ha un livello di accessibilità protetto quindi non è generalmente accessibile. – JaredPar

+1

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 –

1

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.

+1

Se si utilizza C# 6 questa è la situazione ideale per utilizzare le eccezioni filtrati 'catch (IOException ex) quando (i