Aquí podría ser tu PUBLICIDAD


¿Cómo verificar el bloqueo de archivos?

votos
226

¿Hay alguna manera de verificar si un archivo está bloqueado sin usar un bloque try / catch?

En este momento, la única forma que conozco es simplemente abrir el archivo y atrapar alguno System.IO.IOException.

Publicado el 04/08/2008 a las 15:56
fuente por usuario ricree
En otros idiomas...        العربية       

12 respuestas

votos
166

Cuando me encontré con un problema similar, he terminado con el siguiente código:

public bool IsFileLocked(string filePath)
{
    try
    {
        using (File.Open(filePath, FileMode.Open)){}
    }
    catch (IOException e)
    {
        var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);

        return errorCode == 32 || errorCode == 33;
    }

    return false;
}
Respondida el 08/07/2010 a las 10:12
fuente por usuario DixonD


Aquí podría ser tu PUBLICIDAD


votos
121

No, desafortunadamente, y si lo piensas, esa información no valdría nada, ya que el archivo podría bloquearse al siguiente segundo (léase: corto tiempo).

¿Por qué específicamente necesita saber si el archivo está bloqueado de todos modos? Sabiendo eso podría darnos otra forma de darle buenos consejos.

Si tu código se vería así:

if not locked then
    open and update file

Luego, entre las dos líneas, otro proceso podría bloquear fácilmente el archivo, lo que le da el mismo problema que estaba tratando de evitar: las excepciones.

Respondida el 04/08/2008 a las 03:59
fuente por usuario Lasse Vågsæther Karlsen

votos
116

Las otras respuestas se basan en información antigua. Éste proporciona una solución mejor.

Hace mucho tiempo que era imposible conseguir de forma fiable la lista de procesos bloqueo de un archivo, ya que Windows simplemente no realizar un seguimiento de esa información. Para apoyar la API de Administrador de reinicio , esa información es ahora un seguimiento. La API de Administrador de reinicio está disponible a partir de Windows Vista y Server 2008 (Windows Administrador de reinicio: Requisitos de tiempo de ejecución ).

Junté código que toma la ruta de un archivo y devuelve una List<Process>de todos los procesos que están trabando ese archivo.

static public class FileUtil
{
    [StructLayout(LayoutKind.Sequential)]
    struct RM_UNIQUE_PROCESS
    {
        public int dwProcessId;
        public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
    }

    const int RmRebootReasonNone = 0;
    const int CCH_RM_MAX_APP_NAME = 255;
    const int CCH_RM_MAX_SVC_NAME = 63;

    enum RM_APP_TYPE
    {
        RmUnknownApp = 0,
        RmMainWindow = 1,
        RmOtherWindow = 2,
        RmService = 3,
        RmExplorer = 4,
        RmConsole = 5,
        RmCritical = 1000
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    struct RM_PROCESS_INFO
    {
        public RM_UNIQUE_PROCESS Process;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
        public string strAppName;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
        public string strServiceShortName;

        public RM_APP_TYPE ApplicationType;
        public uint AppStatus;
        public uint TSSessionId;
        [MarshalAs(UnmanagedType.Bool)]
        public bool bRestartable;
    }

    [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
    static extern int RmRegisterResources(uint pSessionHandle,
                                          UInt32 nFiles,
                                          string[] rgsFilenames,
                                          UInt32 nApplications,
                                          [In] RM_UNIQUE_PROCESS[] rgApplications,
                                          UInt32 nServices,
                                          string[] rgsServiceNames);

    [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
    static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);

    [DllImport("rstrtmgr.dll")]
    static extern int RmEndSession(uint pSessionHandle);

    [DllImport("rstrtmgr.dll")]
    static extern int RmGetList(uint dwSessionHandle,
                                out uint pnProcInfoNeeded,
                                ref uint pnProcInfo,
                                [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
                                ref uint lpdwRebootReasons);

    /// <summary>
    /// Find out what process(es) have a lock on the specified file.
    /// </summary>
    /// <param name="path">Path of the file.</param>
    /// <returns>Processes locking the file</returns>
    /// <remarks>See also:
    /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
    /// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
    /// 
    /// </remarks>
    static public List<Process> WhoIsLocking(string path)
    {
        uint handle;
        string key = Guid.NewGuid().ToString();
        List<Process> processes = new List<Process>();

        int res = RmStartSession(out handle, 0, key);

        if (res != 0)
            throw new Exception("Could not begin restart session.  Unable to determine file locker.");

        try
        {
            const int ERROR_MORE_DATA = 234;
            uint pnProcInfoNeeded = 0,
                 pnProcInfo = 0,
                 lpdwRebootReasons = RmRebootReasonNone;

            string[] resources = new string[] { path }; // Just checking on one resource.

            res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);

            if (res != 0) 
                throw new Exception("Could not register resource.");                                    

            //Note: there's a race condition here -- the first call to RmGetList() returns
            //      the total number of process. However, when we call RmGetList() again to get
            //      the actual processes this number may have increased.
            res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);

            if (res == ERROR_MORE_DATA)
            {
                // Create an array to store the process results
                RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
                pnProcInfo = pnProcInfoNeeded;

                // Get the list
                res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);

                if (res == 0)
                {
                    processes = new List<Process>((int)pnProcInfo);

                    // Enumerate all of the results and add them to the 
                    // list to be returned
                    for (int i = 0; i < pnProcInfo; i++)
                    {
                        try
                        {
                            processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
                        }
                        // catch the error -- in case the process is no longer running
                        catch (ArgumentException) { }
                    }
                }
                else
                    throw new Exception("Could not list processes locking resource.");                    
            }
            else if (res != 0)
                throw new Exception("Could not list processes locking resource. Failed to get size of result.");                    
        }
        finally
        {
            RmEndSession(handle);
        }

        return processes;
    }
}

ACTUALIZAR

Aquí es otra discusión con código de ejemplo sobre cómo utilizar la API de Administrador de reinicio.

Respondida el 17/12/2013 a las 12:47
fuente por usuario Eric J.

votos
19

También puede comprobar si cualquier proceso está utilizando este archivo y mostrar una lista de programas debe cerrar para continuar como un instalador hace.

public static string GetFileProcessName(string filePath)
{
    Process[] procs = Process.GetProcesses();
    string fileName = Path.GetFileName(filePath);

    foreach (Process proc in procs)
    {
        if (proc.MainWindowHandle != new IntPtr(0) && !proc.HasExited)
        {
            ProcessModule[] arr = new ProcessModule[proc.Modules.Count];

            foreach (ProcessModule pm in proc.Modules)
            {
                if (pm.ModuleName == fileName)
                    return proc.ProcessName;
            }
        }
    }

    return null;
}
Respondida el 01/04/2011 a las 12:19
fuente por usuario Aralmo

votos
15

En lugar de utilizar interoperabilidad se pueden utilizar los métodos de la clase FileStream .NET bloqueo y desbloqueo:

FileStream.Lock http://msdn.microsoft.com/en-us/library/system.io.filestream.lock.aspx

FileStream.Unlock http://msdn.microsoft.com/en-us/library/system.io.filestream.unlock.aspx

Respondida el 08/03/2010 a las 06:09
fuente por usuario Sergio Vicente

votos
7

Aquí está una variación del código de DixonD que se suma el número de segundos de espera para el archivo para desbloquear, y vuelve a intentarlo:

public bool IsFileLocked(string filePath, int secondsToWait)
{
    bool isLocked = true;
    int i = 0;

    while (isLocked &&  ((i < secondsToWait) || (secondsToWait == 0)))
    {
        try
        {
            using (File.Open(filePath, FileMode.Open)) { }
            return false;
        }
        catch (IOException e)
        {
            var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
            isLocked = errorCode == 32 || errorCode == 33;
            i++;

            if (secondsToWait !=0)
                new System.Threading.ManualResetEvent(false).WaitOne(1000);
        }
    }

    return isLocked;
}


if (!IsFileLocked(file, 10))
{
    ...
}
else
{
    throw new Exception(...);
}
Respondida el 24/09/2013 a las 07:34
fuente por usuario 40-Love

votos
7

Una variación de la excelente respuesta de DixonD (arriba).

public static bool TryOpen(string path,
                           FileMode fileMode,
                           FileAccess fileAccess,
                           FileShare fileShare,
                           TimeSpan timeout,
                           out Stream stream)
{
    var endTime = DateTime.Now + timeout;

    while (DateTime.Now < endTime)
    {
        if (TryOpen(path, fileMode, fileAccess, fileShare, out stream))
            return true;
    }

    stream = null;
    return false;
}

public static bool TryOpen(string path,
                           FileMode fileMode,
                           FileAccess fileAccess,
                           FileShare fileShare,
                           out Stream stream)
{
    try
    {
        stream = File.Open(path, fileMode, fileAccess, fileShare);
        return true;
    }
    catch (IOException e)
    {
        if (!FileIsLocked(e))
            throw;

        stream = null;
        return false;
    }
}

private const uint HRFileLocked = 0x80070020;
private const uint HRPortionOfFileLocked = 0x80070021;

private static bool FileIsLocked(IOException ioException)
{
    var errorCode = (uint)Marshal.GetHRForException(ioException);
    return errorCode == HRFileLocked || errorCode == HRPortionOfFileLocked;
}

Uso:

private void Sample(string filePath)
{
    Stream stream = null;

    try
    {
        var timeOut = TimeSpan.FromSeconds(1);

        if (!TryOpen(filePath,
                     FileMode.Open,
                     FileAccess.ReadWrite,
                     FileShare.ReadWrite,
                     timeOut,
                     out stream))
            return;

        // Use stream...
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }
}
Respondida el 03/01/2013 a las 04:41
fuente por usuario Tristan

votos
7

Puede llamar a LockFile a través de la interoperabilidad en la región del archivo que le interese. Esto no arrojará una excepción; si tiene éxito, tendrá un bloqueo en esa parte del archivo (que está en manos de su proceso), ese bloqueo será hasta que llame a UnlockFile o su proceso fallezca.

Respondida el 24/07/2009 a las 07:42
fuente por usuario Sam Saffron

votos
6

Puede ver si el archivo está bloqueado al intentar leerlo o bloquearlo usted mismo primero.

Por favor, mira mi respuesta aquí para más información .

Respondida el 09/03/2009 a las 01:54
fuente por usuario Brian R. Bondy

votos
6

Luego, entre las dos líneas, otro proceso podría bloquear fácilmente el archivo, lo que le da el mismo problema que estaba tratando de evitar: las excepciones.

Sin embargo, de esta manera, sabría que el problema es temporal y lo intentará más tarde. (Por ejemplo, podría escribir un hilo que, si encuentra un bloqueo al intentar escribir, lo sigue intentando hasta que el bloqueo desaparezca).

La IOException, por otro lado, no es en sí misma lo suficientemente específica como para que el bloqueo sea la causa de la falla de IO. Puede haber razones que no son temporales.

Respondida el 17/08/2008 a las 07:17
fuente por usuario Sören Kuklau

votos
0

Lo mismo pero de Powershell

function Test-FileOpen
{
    Param
    ([string]$FileToOpen)
    try
    {
        $openFile =([system.io.file]::Open($FileToOpen,[system.io.filemode]::Open))
        $open =$true
        $openFile.close()
    }
    catch
    {
        $open = $false
    }
    $open
}
Respondida el 23/12/2015 a las 11:24
fuente por usuario thom schumacher

votos
0

Lo que terminé haciendo es:

internal void LoadExternalData() {
    FileStream file;

    if (TryOpenRead("filepath/filename", 5, out file)) {
        using (file)
        using (StreamReader reader = new StreamReader(file)) {
         // do something 
        }
    }
}


internal bool TryOpenRead(string path, int timeout, out FileStream file) {
    bool isLocked = true;
    bool condition = true;

    do {
        try {
            file = File.OpenRead(path);
            return true;
        }
        catch (IOException e) {
            var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
            isLocked = errorCode == 32 || errorCode == 33;
            condition = (isLocked && timeout > 0);

            if (condition) {
                // we only wait if the file is locked. If the exception is of any other type, there's no point on keep trying. just return false and null;
                timeout--;
                new System.Threading.ManualResetEvent(false).WaitOne(1000);
            }
        }
    }
    while (condition);

    file = null;
    return false;
}
Respondida el 16/12/2013 a las 08:19
fuente por usuario Bart Calixto


Aquí podría ser tu PUBLICIDAD