2010-04-02 14 views
10

Cuando abro un archivo, quiero saber si está siendo utilizado por otro proceso para poder realizar un manejo especial; cualquier otra IOException que burbujeará. La propiedad Mensaje de IOException contiene "El proceso no puede acceder al archivo 'foo' porque lo está utilizando otro proceso", pero esto no es adecuado para la detección programática. ¿Cuál es la manera más segura y más robusta de detectar un archivo que está siendo utilizado por otro proceso?Cómo saber si una IOException atrapada es causada por el archivo que está siendo utilizado por otro proceso, sin recurrir al análisis de la propiedad de mensaje de la excepción

+0

¿El tipo es realmente una IOException o es uno de los tipos derivados de IOException? –

+1

Fuera de interés, ¿por qué necesita detectar esto programáticamente? ¿Qué planeas hacer una vez que descubras que otro proceso está usando tu archivo? –

+0

@Mark Byers, ver mi comentario en contra de la respuesta aceptada. –

Respuesta

13

Esta versión particular de IOException se produce cuando el código de error devuelto desde la función nativa de Win32 es ERROR_SHARING_VIOLATION (Documentation). Tiene el valor numérico de 0x20 pero se almacena como 0x80070020 en la propiedad HRESULT de la excepción (que es el resultado de llamar MakeHRFromErrorCode).

Así que la forma programática de comprobar que existe una violación de distribución está comprobando la propiedad HResult en el IOException para el valor 0x80070020.

public static bool IsSharingViolation(this IOException ex) { 
    return 0x80070020 == Marshal.GetHRForException(ex); 
} 

Sin embargo, me pregunto qué es exactamente lo que quiere hacer en el caso de que se haya producido como resultado de una infracción de uso compartido. En el momento en que se lanza la excepción, el otro proceso podría salir y, por lo tanto, eliminar la violación.

+1

¿Por qué no utilizar 'ex.HResult' directamente en lugar de' Marshal.GetHRForException'? –

+2

@Anders porque la propiedad HResult tiene un nivel de accesibilidad protegido, por lo que no es accesible en general. – JaredPar

+1

Fantástica respuesta JaredPar, gracias ... En respuesta a su pregunta; Necesito detectar este escenario porque necesito un mensaje amigable para la capa de presentación. Está bien si el conflicto puede no existir en el momento después, debido a la naturaleza de mi aplicación ... gracias de nuevo –

1

no tengo suficiente "representante" para comentar y espero que esto "respuesta" está bien ...

La respuesta aceptada es exactamente lo que estaba buscando y funciona perfectamente, pero la gente aquí y en similares Las preguntas han cuestionado la utilidad de verificar si un archivo está bloqueado. Es correcto que una función de utilidad para probar si un archivo está bloqueado no es de mucha utilidad porque en la siguiente declaración el estado podría haber cambiado.

Pero el patrón de intentar una operación de bloqueo y luego responder de manera diferente a los errores de bloqueo frente a los errores generales es válido y útil. Lo más obvio es esperar un poco y volver a intentar la operación. Esto se puede generalizar en una función auxiliar de esta manera:

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 

... que luego pueden ser utilizados como esto:

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; 
} 

... de todos modos, la simple publicación en caso de que alguien con necesidades similares encuentra el patrón útil.

+1

Si está utilizando C# 6, esta es la situación perfecta para utilizar las excepciones filtradas 'catch (IOException ex) when (i

Cuestiones relacionadas